From 4cc45d1ffa8d3abf179a2f820b726836fbaae648 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 4 Sep 2024 00:54:53 +0000 Subject: [PATCH] Added chart versions: hashicorp/consul: - 1.5.3 kuma/kuma: - 2.8.3 new-relic/nri-bundle: - 5.0.91 speedscale/speedscale-operator: - 2.2.342 traefik/traefik: - 31.0.0 --- assets/hashicorp/consul-1.5.3.tgz | Bin 0 -> 238832 bytes assets/kuma/kuma-2.8.3.tgz | Bin 0 -> 70018 bytes assets/new-relic/nri-bundle-5.0.91.tgz | Bin 0 -> 347008 bytes .../speedscale-operator-2.2.342.tgz | Bin 0 -> 16770 bytes assets/traefik/traefik-31.0.0.tgz | Bin 0 -> 230201 bytes charts/hashicorp/consul/1.5.3/.helmignore | 5 + charts/hashicorp/consul/1.5.3/Chart.yaml | 37 + charts/hashicorp/consul/1.5.3/README.md | 109 + charts/hashicorp/consul/1.5.3/addons/gen.sh | 37 + .../1.5.3/addons/values/prometheus.yaml | 21 + charts/hashicorp/consul/1.5.3/assets/icon.png | Bin 0 -> 7981 bytes .../consul/1.5.3/templates/NOTES.txt | 21 + .../consul/1.5.3/templates/_helpers.tpl | 704 + .../templates/auth-method-clusterrole.yaml | 18 + .../auth-method-clusterrolebinding.yaml | 39 + .../1.5.3/templates/auth-method-secret.yaml | 16 + .../templates/auth-method-serviceaccount.yaml | 19 + .../templates/client-config-configmap.yaml | 40 + .../1.5.3/templates/client-daemonset.yaml | 611 + .../templates/client-podsecuritypolicy.yaml | 76 + .../consul/1.5.3/templates/client-role.yaml | 43 + .../1.5.3/templates/client-rolebinding.yaml | 20 + .../client-securitycontextconstraints.yaml | 61 + .../templates/client-serviceaccount.yaml | 23 + .../client-tmp-extra-config-configmap.yaml | 21 + .../1.5.3/templates/cni-clusterrole.yaml | 38 + .../templates/cni-clusterrolebinding.yaml | 20 + .../consul/1.5.3/templates/cni-daemonset.yaml | 92 + .../cni-networkattachmentdefinition.yaml | 25 + .../templates/cni-podsecuritypolicy.yaml | 31 + .../1.5.3/templates/cni-resourcequota.yaml | 22 + .../cni-securitycontextconstraints.yaml | 55 + .../1.5.3/templates/cni-serviceaccount.yaml | 19 + .../templates/connect-inject-clusterrole.yaml | 291 + .../connect-inject-clusterrolebinding.yaml | 20 + .../templates/connect-inject-deployment.yaml | 371 + .../connect-inject-leader-election-role.yaml | 41 + ...ct-inject-leader-election-rolebinding.yaml | 21 + ...t-inject-mutatingwebhookconfiguration.yaml | 381 + .../connect-inject-podsecuritypolicy.yaml | 40 + .../templates/connect-inject-service.yaml | 23 + .../connect-inject-serviceaccount.yaml | 23 + ...inject-validatingwebhookconfiguration.yaml | 47 + .../connect-injector-disruptionbudget.yaml | 30 + .../1.5.3/templates/crd-apigateways.yaml | 317 + .../crd-controlplanerequestlimits.yaml | 201 + .../templates/crd-exportedservices-v1.yaml | 147 + .../1.5.3/templates/crd-exportedservices.yaml | 114 + .../templates/crd-gatewayclassconfigs-v1.yaml | 232 + .../templates/crd-gatewayclassconfigs.yaml | 1729 ++ .../crd-gatewayclasses-external.yaml | 328 + .../1.5.3/templates/crd-gatewayclasses.yaml | 130 + .../1.5.3/templates/crd-gatewaypolicies.yaml | 302 + .../templates/crd-gateways-external.yaml | 882 + .../templates/crd-grpcroutes-external.yaml | 766 + .../1.5.3/templates/crd-grpcroutes.yaml | 669 + .../templates/crd-httproutes-external.yaml | 1914 +++ .../1.5.3/templates/crd-httproutes.yaml | 726 + .../1.5.3/templates/crd-ingressgateways.yaml | 466 + .../1.5.3/templates/crd-jwtproviders.yaml | 375 + .../templates/crd-meshconfigurations.yaml | 107 + .../consul/1.5.3/templates/crd-meshes.yaml | 214 + .../1.5.3/templates/crd-meshgateways.yaml | 140 + .../1.5.3/templates/crd-meshservices.yaml | 61 + .../1.5.3/templates/crd-peeringacceptors.yaml | 152 + .../1.5.3/templates/crd-peeringdialers.yaml | 152 + .../templates/crd-proxyconfigurations.yaml | 426 + .../1.5.3/templates/crd-proxydefaults.yaml | 278 + .../crd-referencegrants-external.yaml | 208 + .../1.5.3/templates/crd-registrations.yaml | 257 + .../1.5.3/templates/crd-routeauthfilters.yaml | 215 + .../templates/crd-routeretryfilters.yaml | 121 + .../templates/crd-routetimeoutfilters.yaml | 113 + .../1.5.3/templates/crd-samenessgroups.yaml | 133 + .../1.5.3/templates/crd-servicedefaults.yaml | 580 + .../templates/crd-serviceintentions.yaml | 310 + .../1.5.3/templates/crd-serviceresolvers.yaml | 372 + .../1.5.3/templates/crd-servicerouters.yaml | 335 + .../1.5.3/templates/crd-servicesplitters.yaml | 194 + .../templates/crd-tcproutes-external.yaml | 281 + .../consul/1.5.3/templates/crd-tcproutes.yaml | 299 + .../templates/crd-terminatinggateways.yaml | 148 + .../templates/crd-tlsroutes-external.yaml | 291 + .../templates/crd-trafficpermissions.yaml | 280 + .../templates/crd-udproutes-external.yaml | 281 + .../create-federation-secret-job.yaml | 147 + ...e-federation-secret-podsecuritypolicy.yaml | 42 + .../create-federation-secret-role.yaml | 49 + .../create-federation-secret-rolebinding.yaml | 23 + ...eate-federation-secret-serviceaccount.yaml | 22 + .../1.5.3/templates/datadog-agent-role.yaml | 38 + .../templates/datadog-agent-rolebinding.yaml | 26 + .../consul/1.5.3/templates/dns-service.yaml | 41 + .../templates/enterprise-license-job.yaml | 147 + .../enterprise-license-podsecuritypolicy.yaml | 39 + .../templates/enterprise-license-role.yaml | 37 + .../enterprise-license-rolebinding.yaml | 22 + .../enterprise-license-serviceaccount.yaml | 21 + .../templates/expose-servers-service.yaml | 63 + .../gateway-cleanup-clusterrole.yaml | 44 + .../gateway-cleanup-clusterrolebinding.yaml | 20 + .../1.5.3/templates/gateway-cleanup-job.yaml | 68 + .../gateway-cleanup-podsecuritypolicy.yaml | 32 + .../gateway-cleanup-serviceaccount.yaml | 19 + .../gateway-resources-clusterrole.yaml | 47 + .../gateway-resources-clusterrolebinding.yaml | 20 + .../gateway-resources-configmap.yaml | 195 + .../templates/gateway-resources-job.yaml | 108 + .../gateway-resources-podsecuritypolicy.yaml | 32 + .../gateway-resources-serviceaccount.yaml | 19 + .../gossip-encryption-autogenerate-job.yaml | 71 + ...yption-autogenerate-podsecuritypolicy.yaml | 40 + .../gossip-encryption-autogenerate-role.yaml | 32 + ...p-encryption-autogenerate-rolebinding.yaml | 23 + ...ncryption-autogenerate-serviceaccount.yaml | 22 + .../ingress-gateways-deployment.yaml | 379 + .../ingress-gateways-podsecuritypolicy.yaml | 47 + .../templates/ingress-gateways-role.yaml | 46 + .../ingress-gateways-rolebinding.yaml | 25 + .../templates/ingress-gateways-service.yaml | 51 + .../ingress-gateways-serviceaccount.yaml | 35 + .../templates/mesh-gateway-clusterrole.yaml | 36 + .../mesh-gateway-clusterrolebinding.yaml | 22 + .../templates/mesh-gateway-deployment.yaml | 325 + .../mesh-gateway-podsecuritypolicy.yaml | 56 + .../1.5.3/templates/mesh-gateway-service.yaml | 35 + .../mesh-gateway-serviceaccount.yaml | 25 + .../1.5.3/templates/partition-init-job.yaml | 132 + .../partition-init-podsecuritypolicy.yaml | 40 + .../1.5.3/templates/partition-init-role.yaml | 35 + .../templates/partition-init-rolebinding.yaml | 24 + .../partition-init-serviceaccount.yaml | 23 + .../templates/partition-name-configmap.yaml | 19 + .../consul/1.5.3/templates/prometheus.yaml | 488 + .../server-acl-init-cleanup-job.yaml | 90 + ...er-acl-init-cleanup-podsecuritypolicy.yaml | 40 + .../server-acl-init-cleanup-role.yaml | 28 + .../server-acl-init-cleanup-rolebinding.yaml | 23 + ...erver-acl-init-cleanup-serviceaccount.yaml | 22 + .../1.5.3/templates/server-acl-init-job.yaml | 350 + .../server-acl-init-podsecuritypolicy.yaml | 41 + .../1.5.3/templates/server-acl-init-role.yaml | 38 + .../server-acl-init-rolebinding.yaml | 23 + .../server-acl-init-serviceaccount.yaml | 22 + .../1.5.3/templates/server-clusterrole.yaml | 16 + .../templates/server-clusterrolebinding.yaml | 18 + .../templates/server-config-configmap.yaml | 220 + .../templates/server-disruptionbudget.yaml | 26 + .../templates/server-podsecuritypolicy.yaml | 53 + .../consul/1.5.3/templates/server-role.yaml | 34 + .../1.5.3/templates/server-rolebinding.yaml | 20 + .../server-securitycontextconstraints.yaml | 49 + .../1.5.3/templates/server-service.yaml | 72 + .../templates/server-serviceaccount.yaml | 23 + .../server-snapshot-agent-configmap.yaml | 24 + .../1.5.3/templates/server-statefulset.yaml | 757 + .../server-tmp-extra-config-configmap.yaml | 21 + .../templates/sync-catalog-clusterrole.yaml | 60 + .../sync-catalog-clusterrolebinding.yaml | 21 + .../templates/sync-catalog-deployment.yaml | 238 + .../sync-catalog-podsecuritypolicy.yaml | 40 + .../sync-catalog-serviceaccount.yaml | 24 + .../telemetry-collector-configmap.yaml | 18 + .../telemetry-collector-deployment.yaml | 453 + ...telemetry-collector-podsecuritypolicy.yaml | 42 + .../templates/telemetry-collector-role.yaml | 21 + .../telemetry-collector-rolebinding.yaml | 21 + .../telemetry-collector-service.yaml | 24 + .../telemetry-collector-serviceaccount.yaml | 23 + .../telemetry-collector-v2-deployment.yaml | 415 + .../terminating-gateways-deployment.yaml | 356 + ...erminating-gateways-podsecuritypolicy.yaml | 47 + .../templates/terminating-gateways-role.yaml | 32 + .../terminating-gateways-rolebinding.yaml | 26 + .../terminating-gateways-service.yaml | 31 + .../terminating-gateways-serviceaccount.yaml | 35 + .../1.5.3/templates/tests/test-runner.yaml | 79 + .../1.5.3/templates/tls-init-cleanup-job.yaml | 85 + .../tls-init-cleanup-podsecuritypolicy.yaml | 43 + .../templates/tls-init-cleanup-role.yaml | 41 + .../tls-init-cleanup-rolebinding.yaml | 27 + .../tls-init-cleanup-serviceaccount.yaml | 26 + .../consul/1.5.3/templates/tls-init-job.yaml | 127 + .../templates/tls-init-podsecuritypolicy.yaml | 43 + .../consul/1.5.3/templates/tls-init-role.yaml | 38 + .../1.5.3/templates/tls-init-rolebinding.yaml | 27 + .../templates/tls-init-serviceaccount.yaml | 26 + .../consul/1.5.3/templates/ui-ingress.yaml | 85 + .../consul/1.5.3/templates/ui-service.yaml | 46 + .../webhook-cert-manager-clusterrole.yaml | 54 + ...bhook-cert-manager-clusterrolebinding.yaml | 21 + .../webhook-cert-manager-configmap.yaml | 29 + .../webhook-cert-manager-deployment.yaml | 79 + ...ebhook-cert-manager-podsecuritypolicy.yaml | 43 + .../webhook-cert-manager-serviceaccount.yaml | 20 + charts/hashicorp/consul/1.5.3/values.yaml | 3669 +++++ charts/kuma/kuma/2.8.3/.helmdocsignore | 1 + charts/kuma/kuma/2.8.3/.helmignore | 23 + charts/kuma/kuma/2.8.3/Chart.yaml | 26 + charts/kuma/kuma/2.8.3/README.md | 256 + charts/kuma/kuma/2.8.3/README.md.gotmpl | 52 + .../2.8.3/crds/kuma.io_circuitbreakers.yaml | 50 + .../2.8.3/crds/kuma.io_containerpatches.yaml | 114 + .../2.8.3/crds/kuma.io_dataplaneinsights.yaml | 50 + .../kuma/2.8.3/crds/kuma.io_dataplanes.yaml | 70 + .../2.8.3/crds/kuma.io_externalservices.yaml | 50 + .../2.8.3/crds/kuma.io_faultinjections.yaml | 50 + .../kuma/2.8.3/crds/kuma.io_healthchecks.yaml | 50 + .../crds/kuma.io_hostnamegenerators.yaml | 65 + .../2.8.3/crds/kuma.io_meshaccesslogs.yaml | 556 + .../crds/kuma.io_meshcircuitbreakers.yaml | 738 + .../kuma/kuma/2.8.3/crds/kuma.io_meshes.yaml | 50 + .../crds/kuma.io_meshexternalservices.yaml | 333 + .../crds/kuma.io_meshfaultinjections.yaml | 419 + .../crds/kuma.io_meshgatewayinstances.yaml | 364 + .../2.8.3/crds/kuma.io_meshgatewayroutes.yaml | 50 + .../kuma/2.8.3/crds/kuma.io_meshgateways.yaml | 50 + .../2.8.3/crds/kuma.io_meshhealthchecks.yaml | 382 + .../2.8.3/crds/kuma.io_meshhttproutes.yaml | 664 + .../kuma/2.8.3/crds/kuma.io_meshinsights.yaml | 50 + .../kuma.io_meshloadbalancingstrategies.yaml | 572 + .../kuma/2.8.3/crds/kuma.io_meshmetrics.yaml | 293 + .../2.8.3/crds/kuma.io_meshpassthroughs.yaml | 167 + .../2.8.3/crds/kuma.io_meshproxypatches.yaml | 560 + .../2.8.3/crds/kuma.io_meshratelimits.yaml | 498 + .../kuma/2.8.3/crds/kuma.io_meshretries.yaml | 507 + .../kuma/2.8.3/crds/kuma.io_meshservices.yaml | 195 + .../2.8.3/crds/kuma.io_meshtcproutes.yaml | 281 + .../kuma/2.8.3/crds/kuma.io_meshtimeouts.yaml | 362 + .../kuma/2.8.3/crds/kuma.io_meshtraces.yaml | 284 + .../crds/kuma.io_meshtrafficpermissions.yaml | 203 + .../2.8.3/crds/kuma.io_proxytemplates.yaml | 50 + .../kuma/2.8.3/crds/kuma.io_ratelimits.yaml | 50 + .../kuma/kuma/2.8.3/crds/kuma.io_retries.yaml | 50 + .../2.8.3/crds/kuma.io_serviceinsights.yaml | 50 + .../kuma/2.8.3/crds/kuma.io_timeouts.yaml | 50 + .../kuma/2.8.3/crds/kuma.io_trafficlogs.yaml | 50 + .../crds/kuma.io_trafficpermissions.yaml | 50 + .../2.8.3/crds/kuma.io_trafficroutes.yaml | 50 + .../2.8.3/crds/kuma.io_traffictraces.yaml | 50 + .../2.8.3/crds/kuma.io_virtualoutbounds.yaml | 50 + .../kuma/2.8.3/crds/kuma.io_zoneegresses.yaml | 56 + .../crds/kuma.io_zoneegressinsights.yaml | 50 + .../2.8.3/crds/kuma.io_zoneingresses.yaml | 56 + .../crds/kuma.io_zoneingressinsights.yaml | 51 + .../kuma/2.8.3/crds/kuma.io_zoneinsights.yaml | 50 + .../kuma/kuma/2.8.3/crds/kuma.io_zones.yaml | 50 + charts/kuma/kuma/2.8.3/templates/NOTES.txt | 42 + charts/kuma/kuma/2.8.3/templates/_helpers.tpl | 402 + .../kuma/2.8.3/templates/cni-configmap.yaml | 22 + .../kuma/2.8.3/templates/cni-daemonset.yaml | 152 + .../kuma/kuma/2.8.3/templates/cni-rbac.yaml | 51 + .../kuma/2.8.3/templates/cp-configmap.yaml | 33 + .../kuma/2.8.3/templates/cp-deployment.yaml | 398 + .../templates/cp-global-sync-service.yaml | 33 + charts/kuma/kuma/2.8.3/templates/cp-hpa.yaml | 24 + .../kuma/kuma/2.8.3/templates/cp-ingress.yaml | 25 + .../cp-kds-global-server-secret.yaml | 15 + .../cp-kds-zone-client-tls-secret.yaml | 13 + charts/kuma/kuma/2.8.3/templates/cp-pdb.yaml | 20 + charts/kuma/kuma/2.8.3/templates/cp-rbac.yaml | 315 + .../kuma/kuma/2.8.3/templates/cp-service.yaml | 49 + .../templates/cp-webhooks-and-secrets.yaml | 337 + .../2.8.3/templates/egress-deployment.yaml | 137 + .../kuma/kuma/2.8.3/templates/egress-hpa.yaml | 24 + .../kuma/kuma/2.8.3/templates/egress-pdb.yaml | 20 + .../kuma/2.8.3/templates/egress-rbac.yaml | 18 + .../kuma/2.8.3/templates/egress-service.yaml | 32 + .../kuma/2.8.3/templates/gateway-class.yaml | 19 + .../2.8.3/templates/ingress-deployment.yaml | 141 + .../kuma/2.8.3/templates/ingress-hpa.yaml | 24 + .../kuma/2.8.3/templates/ingress-pdb.yaml | 20 + .../kuma/2.8.3/templates/ingress-rbac.yaml | 18 + .../kuma/2.8.3/templates/ingress-service.yaml | 32 + .../post-delete-cleanup-ebpf-job.yaml | 126 + .../2.8.3/templates/pre-delete-webhooks.yaml | 109 + .../pre-install-patch-namespace-job.yaml | 124 + .../pre-upgrade-install-crds-job.yaml | 171 + charts/kuma/kuma/2.8.3/values.yaml | 748 + .../new-relic/nri-bundle/5.0.91/.helmignore | 22 + charts/new-relic/nri-bundle/5.0.91/Chart.lock | 39 + charts/new-relic/nri-bundle/5.0.91/Chart.yaml | 85 + charts/new-relic/nri-bundle/5.0.91/README.md | 200 + .../nri-bundle/5.0.91/README.md.gotmpl | 166 + .../new-relic/nri-bundle/5.0.91/app-readme.md | 5 + .../charts/k8s-agents-operator/.helmignore | 23 + .../charts/k8s-agents-operator/Chart.yaml | 16 + .../charts/k8s-agents-operator/README.md | 191 + .../k8s-agents-operator/README.md.gotmpl | 157 + .../k8s-agents-operator/templates/NOTES.txt | 36 + .../templates/_helpers.tpl | 80 + .../templates/certmanager.yaml | 17 + .../templates/deployment.yaml | 89 + .../templates/instrumentation-crd.yaml | 1150 ++ .../templates/leader-election-rbac.yaml | 49 + .../templates/manager-rbac.yaml | 76 + .../mutating-webhook-configuration.yaml | 49 + .../templates/newrelic_license_secret.yaml | 14 + .../templates/proxy-rbac.yaml | 34 + .../templates/reader-rbac.yaml | 11 + .../templates/selfsigned-issuer.yaml | 8 + .../templates/service.yaml | 14 + .../validating-webhook-configuration.yaml | 48 + .../templates/webhook-service.yaml | 13 + .../charts/k8s-agents-operator/values.yaml | 62 + .../charts/kube-state-metrics/.helmignore | 21 + .../charts/kube-state-metrics/Chart.yaml | 26 + .../charts/kube-state-metrics/README.md | 85 + .../kube-state-metrics/templates/NOTES.txt | 23 + .../kube-state-metrics/templates/_helpers.tpl | 156 + .../templates/ciliumnetworkpolicy.yaml | 33 + .../templates/clusterrolebinding.yaml | 20 + .../templates/crs-configmap.yaml | 16 + .../templates/deployment.yaml | 279 + .../templates/extra-manifests.yaml | 4 + .../templates/kubeconfig-secret.yaml | 12 + .../templates/networkpolicy.yaml | 43 + .../kube-state-metrics/templates/pdb.yaml | 18 + .../templates/podsecuritypolicy.yaml | 39 + .../templates/psp-clusterrole.yaml | 19 + .../templates/psp-clusterrolebinding.yaml | 16 + .../templates/rbac-configmap.yaml | 22 + .../kube-state-metrics/templates/role.yaml | 212 + .../templates/rolebinding.yaml | 24 + .../kube-state-metrics/templates/service.yaml | 49 + .../templates/serviceaccount.yaml | 15 + .../templates/servicemonitor.yaml | 114 + .../templates/stsdiscovery-role.yaml | 26 + .../templates/stsdiscovery-rolebinding.yaml | 17 + .../templates/verticalpodautoscaler.yaml | 44 + .../charts/kube-state-metrics/values.yaml | 441 + .../newrelic-infra-operator/.helmignore | 1 + .../charts/newrelic-infra-operator/Chart.lock | 6 + .../charts/newrelic-infra-operator/Chart.yaml | 35 + .../charts/newrelic-infra-operator/README.md | 114 + .../newrelic-infra-operator/README.md.gotmpl | 77 + .../charts/common-library/.helmignore | 23 + .../charts/common-library/Chart.yaml | 17 + .../charts/common-library/DEVELOPERS.md | 747 + .../charts/common-library/README.md | 106 + .../common-library/templates/_affinity.tpl | 10 + .../templates/_agent-config.tpl | 26 + .../common-library/templates/_cluster.tpl | 15 + .../templates/_custom-attributes.tpl | 17 + .../common-library/templates/_dnsconfig.tpl | 10 + .../common-library/templates/_fedramp.tpl | 25 + .../common-library/templates/_hostnetwork.tpl | 39 + .../common-library/templates/_images.tpl | 94 + .../common-library/templates/_insights.tpl | 56 + .../templates/_insights_secret.yaml.tpl | 21 + .../common-library/templates/_labels.tpl | 54 + .../common-library/templates/_license.tpl | 68 + .../templates/_license_secret.yaml.tpl | 21 + .../templates/_low-data-mode.tpl | 26 + .../common-library/templates/_naming.tpl | 73 + .../templates/_nodeselector.tpl | 10 + .../templates/_priority-class-name.tpl | 10 + .../common-library/templates/_privileged.tpl | 28 + .../common-library/templates/_proxy.tpl | 10 + .../common-library/templates/_region.tpl | 74 + .../templates/_security-context.tpl | 23 + .../templates/_serviceaccount.tpl | 90 + .../common-library/templates/_staging.tpl | 39 + .../common-library/templates/_tolerations.tpl | 10 + .../common-library/templates/_userkey.tpl | 56 + .../templates/_userkey_secret.yaml.tpl | 21 + .../common-library/templates/_verbose-log.tpl | 54 + .../charts/common-library/values.yaml | 1 + .../ci/test-values.yaml | 39 + .../templates/NOTES.txt | 4 + .../templates/_helpers.tpl | 136 + .../job-patch/clusterrole.yaml | 27 + .../job-patch/clusterrolebinding.yaml | 20 + .../job-patch/job-createSecret.yaml | 57 + .../job-patch/job-patchWebhook.yaml | 57 + .../admission-webhooks/job-patch/psp.yaml | 50 + .../admission-webhooks/job-patch/role.yaml | 21 + .../job-patch/rolebinding.yaml | 21 + .../job-patch/serviceaccount.yaml | 14 + .../mutatingWebhookConfiguration.yaml | 32 + .../templates/cert-manager.yaml | 52 + .../templates/clusterrole.yaml | 39 + .../templates/clusterrolebinding.yaml | 26 + .../templates/configmap.yaml | 9 + .../templates/deployment.yaml | 92 + .../templates/secret.yaml | 2 + .../templates/service.yaml | 13 + .../templates/serviceaccount.yaml | 13 + .../tests/deployment_test.yaml | 32 + .../tests/job_patch_psp_test.yaml | 23 + .../tests/job_serviceaccount_test.yaml | 64 + .../tests/rbac_test.yaml | 41 + .../newrelic-infra-operator/values.yaml | 222 + .../newrelic-infrastructure/.helmignore | 1 + .../charts/newrelic-infrastructure/Chart.lock | 6 + .../charts/newrelic-infrastructure/Chart.yaml | 26 + .../charts/newrelic-infrastructure/README.md | 220 + .../newrelic-infrastructure/README.md.gotmpl | 137 + .../charts/common-library/.helmignore | 23 + .../charts/common-library/Chart.yaml | 17 + .../charts/common-library/DEVELOPERS.md | 663 + .../charts/common-library/README.md | 106 + .../common-library/templates/_affinity.tpl | 10 + .../templates/_agent-config.tpl | 26 + .../common-library/templates/_cluster.tpl | 15 + .../templates/_custom-attributes.tpl | 17 + .../common-library/templates/_dnsconfig.tpl | 10 + .../common-library/templates/_fedramp.tpl | 25 + .../common-library/templates/_hostnetwork.tpl | 39 + .../common-library/templates/_images.tpl | 94 + .../common-library/templates/_insights.tpl | 56 + .../templates/_insights_secret.yaml.tpl | 21 + .../common-library/templates/_labels.tpl | 54 + .../common-library/templates/_license.tpl | 56 + .../templates/_license_secret.yaml.tpl | 21 + .../templates/_low-data-mode.tpl | 26 + .../common-library/templates/_naming.tpl | 73 + .../templates/_nodeselector.tpl | 10 + .../templates/_priority-class-name.tpl | 10 + .../common-library/templates/_privileged.tpl | 28 + .../common-library/templates/_proxy.tpl | 10 + .../templates/_security-context.tpl | 23 + .../templates/_serviceaccount.tpl | 90 + .../common-library/templates/_staging.tpl | 39 + .../common-library/templates/_tolerations.tpl | 10 + .../common-library/templates/_verbose-log.tpl | 54 + .../charts/common-library/values.yaml | 1 + .../test-cplane-kind-deployment-values.yaml | 135 + .../ci/test-values.yaml | 134 + .../templates/NOTES.txt | 131 + .../templates/_helpers.tpl | 118 + .../templates/_helpers_compatibility.tpl | 202 + .../templates/clusterrole.yaml | 35 + .../templates/clusterrolebinding.yaml | 16 + .../controlplane/_affinity_helper.tpl | 11 + .../controlplane/_agent-config_helper.tpl | 20 + .../templates/controlplane/_host_network.tpl | 22 + .../templates/controlplane/_naming.tpl | 16 + .../templates/controlplane/_rbac.tpl | 40 + .../controlplane/_tolerations_helper.tpl | 11 + .../controlplane/agent-configmap.yaml | 18 + .../templates/controlplane/clusterrole.yaml | 47 + .../controlplane/clusterrolebinding.yaml | 16 + .../templates/controlplane/daemonset.yaml | 205 + .../templates/controlplane/rolebinding.yaml | 21 + .../controlplane/scraper-configmap.yaml | 36 + .../controlplane/serviceaccount.yaml | 13 + .../templates/ksm/_affinity_helper.tpl | 14 + .../templates/ksm/_agent-config_helper.tpl | 20 + .../templates/ksm/_host_network.tpl | 22 + .../templates/ksm/_naming.tpl | 8 + .../templates/ksm/_tolerations_helper.tpl | 11 + .../templates/ksm/agent-configmap.yaml | 18 + .../templates/ksm/deployment.yaml | 192 + .../templates/ksm/scraper-configmap.yaml | 15 + .../templates/kubelet/_affinity_helper.tpl | 33 + .../kubelet/_agent-config_helper.tpl | 31 + .../templates/kubelet/_host_network.tpl | 22 + .../templates/kubelet/_naming.tpl | 12 + .../kubelet/_security_context_helper.tpl | 32 + .../templates/kubelet/_tolerations_helper.tpl | 11 + .../templates/kubelet/agent-configmap.yaml | 18 + .../templates/kubelet/daemonset.yaml | 265 + .../kubelet/integrations-configmap.yaml | 72 + .../templates/kubelet/scraper-configmap.yaml | 18 + .../templates/podsecuritypolicy.yaml | 26 + .../templates/secret.yaml | 2 + .../templates/serviceaccount.yaml | 13 + .../newrelic-infrastructure/values.yaml | 602 + .../newrelic-k8s-metrics-adapter/.helmignore | 25 + .../newrelic-k8s-metrics-adapter/Chart.lock | 6 + .../newrelic-k8s-metrics-adapter/Chart.yaml | 25 + .../newrelic-k8s-metrics-adapter/README.md | 139 + .../README.md.gotmpl | 107 + .../charts/common-library/.helmignore | 23 + .../charts/common-library/Chart.yaml | 17 + .../charts/common-library/DEVELOPERS.md | 747 + .../charts/common-library/README.md | 106 + .../common-library/templates/_affinity.tpl | 10 + .../templates/_agent-config.tpl | 26 + .../common-library/templates/_cluster.tpl | 15 + .../templates/_custom-attributes.tpl | 17 + .../common-library/templates/_dnsconfig.tpl | 10 + .../common-library/templates/_fedramp.tpl | 25 + .../common-library/templates/_hostnetwork.tpl | 39 + .../common-library/templates/_images.tpl | 94 + .../common-library/templates/_insights.tpl | 56 + .../templates/_insights_secret.yaml.tpl | 21 + .../common-library/templates/_labels.tpl | 54 + .../common-library/templates/_license.tpl | 68 + .../templates/_license_secret.yaml.tpl | 21 + .../templates/_low-data-mode.tpl | 26 + .../common-library/templates/_naming.tpl | 73 + .../templates/_nodeselector.tpl | 10 + .../templates/_priority-class-name.tpl | 10 + .../common-library/templates/_privileged.tpl | 28 + .../common-library/templates/_proxy.tpl | 10 + .../common-library/templates/_region.tpl | 74 + .../templates/_security-context.tpl | 23 + .../templates/_serviceaccount.tpl | 90 + .../common-library/templates/_staging.tpl | 39 + .../common-library/templates/_tolerations.tpl | 10 + .../common-library/templates/_userkey.tpl | 56 + .../templates/_userkey_secret.yaml.tpl | 21 + .../common-library/templates/_verbose-log.tpl | 54 + .../charts/common-library/values.yaml | 1 + .../ci/test-values.yaml | 14 + .../templates/_helpers.tpl | 57 + .../templates/adapter-clusterrolebinding.yaml | 14 + .../templates/adapter-rolebinding.yaml | 15 + .../templates/apiservice/apiservice.yaml | 19 + .../apiservice/job-patch/clusterrole.yaml | 26 + .../job-patch/clusterrolebinding.yaml | 19 + .../job-patch/job-createSecret.yaml | 55 + .../job-patch/job-patchAPIService.yaml | 53 + .../templates/apiservice/job-patch/psp.yaml | 49 + .../templates/apiservice/job-patch/role.yaml | 20 + .../apiservice/job-patch/rolebinding.yaml | 20 + .../apiservice/job-patch/serviceaccount.yaml | 18 + .../templates/configmap.yaml | 19 + .../templates/deployment.yaml | 113 + .../templates/hpa-clusterrole.yaml | 15 + .../templates/hpa-clusterrolebinding.yaml | 14 + .../templates/secret.yaml | 10 + .../templates/service.yaml | 13 + .../templates/serviceaccount.yaml | 13 + .../tests/apiservice_test.yaml | 22 + .../tests/common_extra_naming_test.yaml | 27 + .../tests/configmap_test.yaml | 104 + .../tests/deployment_test.yaml | 99 + .../tests/hpa_clusterrolebinding_test.yaml | 18 + .../job_patch_cluster_rolebinding_test.yaml | 22 + .../tests/job_patch_clusterrole_test.yaml | 20 + .../tests/job_patch_common_test.yaml | 27 + .../job_patch_job_createsecret_test.yaml | 47 + .../job_patch_job_patchapiservice_test.yaml | 56 + .../tests/job_serviceaccount_test.yaml | 79 + .../tests/rbac_test.yaml | 50 + .../newrelic-k8s-metrics-adapter/values.yaml | 156 + .../5.0.91/charts/newrelic-logging/Chart.lock | 6 + .../5.0.91/charts/newrelic-logging/Chart.yaml | 20 + .../5.0.91/charts/newrelic-logging/README.md | 268 + .../charts/common-library/.helmignore | 23 + .../charts/common-library/Chart.yaml | 17 + .../charts/common-library/DEVELOPERS.md | 663 + .../charts/common-library/README.md | 106 + .../common-library/templates/_affinity.tpl | 10 + .../templates/_agent-config.tpl | 26 + .../common-library/templates/_cluster.tpl | 15 + .../templates/_custom-attributes.tpl | 17 + .../common-library/templates/_dnsconfig.tpl | 10 + .../common-library/templates/_fedramp.tpl | 25 + .../common-library/templates/_hostnetwork.tpl | 39 + .../common-library/templates/_images.tpl | 94 + .../common-library/templates/_insights.tpl | 56 + .../templates/_insights_secret.yaml.tpl | 21 + .../common-library/templates/_labels.tpl | 54 + .../common-library/templates/_license.tpl | 56 + .../templates/_license_secret.yaml.tpl | 21 + .../templates/_low-data-mode.tpl | 26 + .../common-library/templates/_naming.tpl | 73 + .../templates/_nodeselector.tpl | 10 + .../templates/_priority-class-name.tpl | 10 + .../common-library/templates/_privileged.tpl | 28 + .../common-library/templates/_proxy.tpl | 10 + .../templates/_security-context.tpl | 23 + .../templates/_serviceaccount.tpl | 90 + .../common-library/templates/_staging.tpl | 39 + .../common-library/templates/_tolerations.tpl | 10 + .../common-library/templates/_verbose-log.tpl | 54 + .../charts/common-library/values.yaml | 1 + .../ci/test-enable-windows-values.yaml | 2 + .../ci/test-lowdatamode-values.yaml | 1 + .../ci/test-override-global-lowdatamode.yaml | 3 + .../ci/test-staging-values.yaml | 1 + .../ci/test-with-empty-global.yaml | 1 + .../ci/test-with-empty-values.yaml | 0 ...and-plugin-metrics-dashboard-template.json | 2237 +++ .../newrelic-logging/templates/NOTES.txt | 18 + .../newrelic-logging/templates/_helpers.tpl | 215 + .../templates/clusterrole.yaml | 23 + .../templates/clusterrolebinding.yaml | 15 + .../newrelic-logging/templates/configmap.yaml | 38 + .../templates/daemonset-windows.yaml | 174 + .../newrelic-logging/templates/daemonset.yaml | 211 + .../templates/persistentvolume.yaml | 57 + .../templates/podsecuritypolicy.yaml | 24 + .../newrelic-logging/templates/secret.yaml | 12 + .../templates/serviceaccount.yaml | 17 + .../tests/cri_parser_test.yaml | 37 + .../tests/dns_config_test.yaml | 62 + .../tests/endpoint_region_selection_test.yaml | 128 + .../fluentbit_k8logging_exclude_test.yaml | 45 + .../tests/fluentbit_persistence_test.yaml | 317 + .../tests/fluentbit_pod_label_test.yaml | 48 + .../tests/fluentbit_sendmetrics_test.yaml | 74 + .../tests/host_network_test.yaml | 46 + .../newrelic-logging/tests/images_test.yaml | 96 + .../tests/linux_volume_mount_test.yaml | 37 + .../newrelic-logging/tests/rbac_test.yaml | 48 + .../charts/newrelic-logging/values.yaml | 361 + .../5.0.91/charts/newrelic-pixie/Chart.yaml | 18 + .../5.0.91/charts/newrelic-pixie/README.md | 166 + .../charts/newrelic-pixie/ci/test-values.yaml | 5 + .../charts/newrelic-pixie/templates/NOTES.txt | 27 + .../newrelic-pixie/templates/_helpers.tpl | 172 + .../newrelic-pixie/templates/configmap.yaml | 12 + .../charts/newrelic-pixie/templates/job.yaml | 164 + .../newrelic-pixie/templates/secret.yaml | 20 + .../newrelic-pixie/tests/configmap.yaml | 44 + .../charts/newrelic-pixie/tests/jobs.yaml | 138 + .../5.0.91/charts/newrelic-pixie/values.yaml | 70 + .../newrelic-prometheus-agent/.helmignore | 23 + .../newrelic-prometheus-agent/Chart.lock | 6 + .../newrelic-prometheus-agent/Chart.yaml | 22 + .../newrelic-prometheus-agent/README.md | 244 + .../README.md.gotmpl | 209 + .../charts/common-library/.helmignore | 23 + .../charts/common-library/Chart.yaml | 17 + .../charts/common-library/DEVELOPERS.md | 663 + .../charts/common-library/README.md | 106 + .../common-library/templates/_affinity.tpl | 10 + .../templates/_agent-config.tpl | 26 + .../common-library/templates/_cluster.tpl | 15 + .../templates/_custom-attributes.tpl | 17 + .../common-library/templates/_dnsconfig.tpl | 10 + .../common-library/templates/_fedramp.tpl | 25 + .../common-library/templates/_hostnetwork.tpl | 39 + .../common-library/templates/_images.tpl | 94 + .../common-library/templates/_insights.tpl | 56 + .../templates/_insights_secret.yaml.tpl | 21 + .../common-library/templates/_labels.tpl | 54 + .../common-library/templates/_license.tpl | 56 + .../templates/_license_secret.yaml.tpl | 21 + .../templates/_low-data-mode.tpl | 26 + .../common-library/templates/_naming.tpl | 73 + .../templates/_nodeselector.tpl | 10 + .../templates/_priority-class-name.tpl | 10 + .../common-library/templates/_privileged.tpl | 28 + .../common-library/templates/_proxy.tpl | 10 + .../templates/_security-context.tpl | 23 + .../templates/_serviceaccount.tpl | 90 + .../common-library/templates/_staging.tpl | 39 + .../common-library/templates/_tolerations.tpl | 10 + .../common-library/templates/_verbose-log.tpl | 54 + .../charts/common-library/values.yaml | 1 + .../ci/test-values.yaml | 6 + .../static/lowdatamodedefaults.yaml | 6 + .../static/metrictyperelabeldefaults.yaml | 17 + .../templates/_helpers.tpl | 165 + .../templates/clusterrole.yaml | 24 + .../templates/clusterrolebinding.yaml | 16 + .../templates/configmap.yaml | 31 + .../templates/secret.yaml | 2 + .../templates/serviceaccount.yaml | 13 + .../templates/statefulset.yaml | 157 + .../tests/configmap_test.yaml | 572 + .../tests/configurator_image_test.yaml | 57 + .../tests/integration_filters_test.yaml | 119 + .../tests/lowdatamode_configmap_test.yaml | 138 + .../newrelic-prometheus-agent/values.yaml | 473 + .../5.0.91/charts/nri-kube-events/Chart.lock | 6 + .../5.0.91/charts/nri-kube-events/Chart.yaml | 26 + .../5.0.91/charts/nri-kube-events/README.md | 79 + .../charts/nri-kube-events/README.md.gotmpl | 43 + .../charts/common-library/.helmignore | 23 + .../charts/common-library/Chart.yaml | 17 + .../charts/common-library/DEVELOPERS.md | 747 + .../charts/common-library/README.md | 106 + .../common-library/templates/_affinity.tpl | 10 + .../templates/_agent-config.tpl | 26 + .../common-library/templates/_cluster.tpl | 15 + .../templates/_custom-attributes.tpl | 17 + .../common-library/templates/_dnsconfig.tpl | 10 + .../common-library/templates/_fedramp.tpl | 25 + .../common-library/templates/_hostnetwork.tpl | 39 + .../common-library/templates/_images.tpl | 94 + .../common-library/templates/_insights.tpl | 56 + .../templates/_insights_secret.yaml.tpl | 21 + .../common-library/templates/_labels.tpl | 54 + .../common-library/templates/_license.tpl | 68 + .../templates/_license_secret.yaml.tpl | 21 + .../templates/_low-data-mode.tpl | 26 + .../common-library/templates/_naming.tpl | 73 + .../templates/_nodeselector.tpl | 10 + .../templates/_priority-class-name.tpl | 10 + .../common-library/templates/_privileged.tpl | 28 + .../common-library/templates/_proxy.tpl | 10 + .../common-library/templates/_region.tpl | 74 + .../templates/_security-context.tpl | 23 + .../templates/_serviceaccount.tpl | 90 + .../common-library/templates/_staging.tpl | 39 + .../common-library/templates/_tolerations.tpl | 10 + .../common-library/templates/_userkey.tpl | 56 + .../templates/_userkey_secret.yaml.tpl | 21 + .../common-library/templates/_verbose-log.tpl | 54 + .../charts/common-library/values.yaml | 1 + .../ci/test-bare-minimum-values.yaml | 3 + .../ci/test-custom-attributes-as-map.yaml | 12 + .../ci/test-custom-attributes-as-string.yaml | 11 + .../nri-kube-events/ci/test-values.yaml | 60 + .../nri-kube-events/templates/NOTES.txt | 3 + .../nri-kube-events/templates/_helpers.tpl | 45 + .../templates/_helpers_compatibility.tpl | 262 + .../templates/agent-configmap.yaml | 12 + .../templates/clusterrole.yaml | 42 + .../templates/clusterrolebinding.yaml | 16 + .../nri-kube-events/templates/configmap.yaml | 23 + .../nri-kube-events/templates/deployment.yaml | 124 + .../nri-kube-events/templates/secret.yaml | 2 + .../templates/serviceaccount.yaml | 11 + .../tests/agent_configmap_test.yaml | 46 + .../nri-kube-events/tests/configmap_test.yaml | 139 + .../tests/deployment_test.yaml | 104 + .../nri-kube-events/tests/images_test.yaml | 168 + .../tests/security_context_test.yaml | 77 + .../5.0.91/charts/nri-kube-events/values.yaml | 135 + .../charts/nri-metadata-injection/.helmignore | 1 + .../charts/nri-metadata-injection/Chart.lock | 6 + .../charts/nri-metadata-injection/Chart.yaml | 25 + .../charts/nri-metadata-injection/README.md | 68 + .../nri-metadata-injection/README.md.gotmpl | 41 + .../charts/common-library/.helmignore | 23 + .../charts/common-library/Chart.yaml | 17 + .../charts/common-library/DEVELOPERS.md | 747 + .../charts/common-library/README.md | 106 + .../common-library/templates/_affinity.tpl | 10 + .../templates/_agent-config.tpl | 26 + .../common-library/templates/_cluster.tpl | 15 + .../templates/_custom-attributes.tpl | 17 + .../common-library/templates/_dnsconfig.tpl | 10 + .../common-library/templates/_fedramp.tpl | 25 + .../common-library/templates/_hostnetwork.tpl | 39 + .../common-library/templates/_images.tpl | 94 + .../common-library/templates/_insights.tpl | 56 + .../templates/_insights_secret.yaml.tpl | 21 + .../common-library/templates/_labels.tpl | 54 + .../common-library/templates/_license.tpl | 68 + .../templates/_license_secret.yaml.tpl | 21 + .../templates/_low-data-mode.tpl | 26 + .../common-library/templates/_naming.tpl | 73 + .../templates/_nodeselector.tpl | 10 + .../templates/_priority-class-name.tpl | 10 + .../common-library/templates/_privileged.tpl | 28 + .../common-library/templates/_proxy.tpl | 10 + .../common-library/templates/_region.tpl | 74 + .../templates/_security-context.tpl | 23 + .../templates/_serviceaccount.tpl | 90 + .../common-library/templates/_staging.tpl | 39 + .../common-library/templates/_tolerations.tpl | 10 + .../common-library/templates/_userkey.tpl | 56 + .../templates/_userkey_secret.yaml.tpl | 21 + .../common-library/templates/_verbose-log.tpl | 54 + .../charts/common-library/values.yaml | 1 + .../ci/test-values.yaml | 5 + .../templates/NOTES.txt | 23 + .../templates/_helpers.tpl | 72 + .../job-patch/clusterrole.yaml | 27 + .../job-patch/clusterrolebinding.yaml | 20 + .../job-patch/job-createSecret.yaml | 61 + .../job-patch/job-patchWebhook.yaml | 61 + .../admission-webhooks/job-patch/psp.yaml | 50 + .../admission-webhooks/job-patch/role.yaml | 21 + .../job-patch/rolebinding.yaml | 21 + .../job-patch/serviceaccount.yaml | 14 + .../mutatingWebhookConfiguration.yaml | 36 + .../templates/cert-manager.yaml | 53 + .../templates/deployment.yaml | 85 + .../templates/service.yaml | 13 + .../tests/cluster_test.yaml | 39 + .../tests/job_serviceaccount_test.yaml | 59 + .../tests/rbac_test.yaml | 38 + .../tests/volume_mounts_test.yaml | 30 + .../charts/nri-metadata-injection/values.yaml | 102 + .../5.0.91/charts/nri-prometheus/.helmignore | 22 + .../5.0.91/charts/nri-prometheus/Chart.lock | 6 + .../5.0.91/charts/nri-prometheus/Chart.yaml | 29 + .../5.0.91/charts/nri-prometheus/README.md | 116 + .../charts/nri-prometheus/README.md.gotmpl | 83 + .../charts/common-library/.helmignore | 23 + .../charts/common-library/Chart.yaml | 17 + .../charts/common-library/DEVELOPERS.md | 663 + .../charts/common-library/README.md | 106 + .../common-library/templates/_affinity.tpl | 10 + .../templates/_agent-config.tpl | 26 + .../common-library/templates/_cluster.tpl | 15 + .../templates/_custom-attributes.tpl | 17 + .../common-library/templates/_dnsconfig.tpl | 10 + .../common-library/templates/_fedramp.tpl | 25 + .../common-library/templates/_hostnetwork.tpl | 39 + .../common-library/templates/_images.tpl | 94 + .../common-library/templates/_insights.tpl | 56 + .../templates/_insights_secret.yaml.tpl | 21 + .../common-library/templates/_labels.tpl | 54 + .../common-library/templates/_license.tpl | 56 + .../templates/_license_secret.yaml.tpl | 21 + .../templates/_low-data-mode.tpl | 26 + .../common-library/templates/_naming.tpl | 73 + .../templates/_nodeselector.tpl | 10 + .../templates/_priority-class-name.tpl | 10 + .../common-library/templates/_privileged.tpl | 28 + .../common-library/templates/_proxy.tpl | 10 + .../templates/_security-context.tpl | 23 + .../templates/_serviceaccount.tpl | 90 + .../common-library/templates/_staging.tpl | 39 + .../common-library/templates/_tolerations.tpl | 10 + .../common-library/templates/_verbose-log.tpl | 54 + .../charts/common-library/values.yaml | 1 + .../ci/test-lowdatamode-values.yaml | 9 + .../ci/test-override-global-lowdatamode.yaml | 10 + .../charts/nri-prometheus/ci/test-values.yaml | 104 + .../static/lowdatamodedefaults.yaml | 10 + .../nri-prometheus/templates/_helpers.tpl | 15 + .../nri-prometheus/templates/clusterrole.yaml | 23 + .../templates/clusterrolebinding.yaml | 16 + .../nri-prometheus/templates/configmap.yaml | 21 + .../nri-prometheus/templates/deployment.yaml | 98 + .../nri-prometheus/templates/secret.yaml | 2 + .../templates/serviceaccount.yaml | 13 + .../nri-prometheus/tests/configmap_test.yaml | 86 + .../nri-prometheus/tests/deployment_test.yaml | 82 + .../nri-prometheus/tests/labels_test.yaml | 32 + .../5.0.91/charts/nri-prometheus/values.yaml | 251 + .../charts/pixie-operator-chart/Chart.yaml | 4 + .../pixie-operator-chart/crds/olm_crd.yaml | 9045 +++++++++++ .../pixie-operator-chart/crds/vizier_crd.yaml | 347 + .../templates/00_olm.yaml | 232 + .../templates/01_px_olm.yaml | 13 + .../templates/02_catalog.yaml | 37 + .../templates/03_subscription.yaml | 11 + .../templates/04_vizier.yaml | 100 + .../templates/deleter.yaml | 25 + .../templates/deleter_role.yaml | 77 + .../charts/pixie-operator-chart/values.yaml | 75 + .../nri-bundle/5.0.91/ci/test-values.yaml | 21 + .../nri-bundle/5.0.91/questions.yaml | 113 + .../new-relic/nri-bundle/5.0.91/values.yaml | 169 + .../speedscale-operator/2.2.342/.helmignore | 23 + .../speedscale-operator/2.2.342/Chart.yaml | 27 + .../speedscale-operator/2.2.342/LICENSE | 201 + .../speedscale-operator/2.2.342/README.md | 111 + .../speedscale-operator/2.2.342/app-readme.md | 111 + .../2.2.342/questions.yaml | 9 + .../2.2.342/templates/NOTES.txt | 12 + .../2.2.342/templates/admission.yaml | 209 + .../2.2.342/templates/configmap.yaml | 41 + .../templates/crds/trafficreplays.yaml | 515 + .../2.2.342/templates/deployments.yaml | 132 + .../2.2.342/templates/hooks.yaml | 73 + .../2.2.342/templates/rbac.yaml | 244 + .../2.2.342/templates/secrets.yaml | 18 + .../2.2.342/templates/services.yaml | 22 + .../2.2.342/templates/tls.yaml | 183 + .../speedscale-operator/2.2.342/values.yaml | 133 + charts/traefik/traefik/31.0.0/.helmignore | 2 + charts/traefik/traefik/31.0.0/Changelog.md | 9257 +++++++++++ charts/traefik/traefik/31.0.0/Chart.yaml | 33 + charts/traefik/traefik/31.0.0/EXAMPLES.md | 1005 ++ charts/traefik/traefik/31.0.0/Guidelines.md | 92 + charts/traefik/traefik/31.0.0/LICENSE | 202 + charts/traefik/traefik/31.0.0/README.md | 158 + charts/traefik/traefik/31.0.0/VALUES.md | 278 + charts/traefik/traefik/31.0.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.0.0/crds/hub.traefik.io_apis.yaml | 190 + .../crds/hub.traefik.io_apiversions.yaml | 194 + .../31.0.0/crds/traefik.io_ingressroutes.yaml | 366 + .../crds/traefik.io_ingressroutetcps.yaml | 247 + .../crds/traefik.io_ingressrouteudps.yaml | 111 + .../31.0.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.0.0/crds/traefik.io_tlsoptions.yaml | 114 + .../31.0.0/crds/traefik.io_tlsstores.yaml | 97 + .../crds/traefik.io_traefikservices.yaml | 639 + .../traefik/31.0.0/templates/NOTES.txt | 36 + .../traefik/31.0.0/templates/_helpers.tpl | 161 + .../traefik/31.0.0/templates/_podtemplate.tpl | 837 + .../31.0.0/templates/_service-metrics.tpl | 25 + .../traefik/31.0.0/templates/_service.tpl | 84 + .../traefik/31.0.0/templates/daemonset.yaml | 50 + .../traefik/31.0.0/templates/deployment.yaml | 54 + .../31.0.0/templates/extra-objects.yaml | 4 + .../traefik/31.0.0/templates/gateway.yaml | 58 + .../31.0.0/templates/gatewayclass.yaml | 14 + .../traefik/traefik/31.0.0/templates/hpa.yaml | 35 + .../templates/hub-admission-controller.yaml | 250 + .../31.0.0/templates/hub-apiportal.yaml | 19 + .../31.0.0/templates/ingressclass.yaml | 12 + .../31.0.0/templates/ingressroute.yaml | 43 + .../31.0.0/templates/poddisruptionbudget.yaml | 23 + .../31.0.0/templates/prometheusrules.yaml | 28 + .../31.0.0/templates/provider-file-cm.yaml | 12 + .../traefik/traefik/31.0.0/templates/pvc.yaml | 26 + .../31.0.0/templates/rbac/clusterrole.yaml | 254 + .../templates/rbac/clusterrolebinding.yaml | 17 + .../templates/rbac/podsecuritypolicy.yaml | 68 + .../traefik/31.0.0/templates/rbac/role.yaml | 143 + .../31.0.0/templates/rbac/rolebinding.yaml | 25 + .../31.0.0/templates/rbac/serviceaccount.yaml | 14 + .../31.0.0/templates/requirements.yaml | 29 + .../31.0.0/templates/service-metrics.yaml | 33 + .../traefik/31.0.0/templates/service.yaml | 83 + .../31.0.0/templates/servicemonitor.yaml | 69 + .../traefik/31.0.0/templates/tlsoption.yaml | 39 + .../traefik/31.0.0/templates/tlsstore.yaml | 12 + charts/traefik/traefik/31.0.0/values.yaml | 971 ++ index.yaml | 230 +- 913 files changed, 124120 insertions(+), 1 deletion(-) create mode 100644 assets/hashicorp/consul-1.5.3.tgz create mode 100644 assets/kuma/kuma-2.8.3.tgz create mode 100644 assets/new-relic/nri-bundle-5.0.91.tgz create mode 100644 assets/speedscale/speedscale-operator-2.2.342.tgz create mode 100644 assets/traefik/traefik-31.0.0.tgz create mode 100644 charts/hashicorp/consul/1.5.3/.helmignore create mode 100644 charts/hashicorp/consul/1.5.3/Chart.yaml create mode 100644 charts/hashicorp/consul/1.5.3/README.md create mode 100644 charts/hashicorp/consul/1.5.3/addons/gen.sh create mode 100644 charts/hashicorp/consul/1.5.3/addons/values/prometheus.yaml create mode 100644 charts/hashicorp/consul/1.5.3/assets/icon.png create mode 100644 charts/hashicorp/consul/1.5.3/templates/NOTES.txt create mode 100644 charts/hashicorp/consul/1.5.3/templates/_helpers.tpl create mode 100644 charts/hashicorp/consul/1.5.3/templates/auth-method-clusterrole.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/auth-method-clusterrolebinding.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/auth-method-secret.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/auth-method-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/client-config-configmap.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/client-daemonset.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/client-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/client-role.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/client-rolebinding.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/client-securitycontextconstraints.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/client-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/client-tmp-extra-config-configmap.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/cni-clusterrole.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/cni-clusterrolebinding.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/cni-daemonset.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/cni-networkattachmentdefinition.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/cni-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/cni-resourcequota.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/cni-securitycontextconstraints.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/cni-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/connect-inject-clusterrole.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/connect-inject-clusterrolebinding.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/connect-inject-deployment.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/connect-inject-leader-election-role.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/connect-inject-leader-election-rolebinding.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/connect-inject-mutatingwebhookconfiguration.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/connect-inject-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/connect-inject-service.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/connect-inject-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/connect-inject-validatingwebhookconfiguration.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/connect-injector-disruptionbudget.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-apigateways.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-controlplanerequestlimits.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-exportedservices-v1.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-exportedservices.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-gatewayclassconfigs-v1.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-gatewayclassconfigs.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-gatewayclasses-external.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-gatewayclasses.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-gatewaypolicies.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-gateways-external.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-grpcroutes-external.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-grpcroutes.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-httproutes-external.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-httproutes.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-ingressgateways.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-jwtproviders.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-meshconfigurations.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-meshes.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-meshgateways.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-meshservices.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-peeringacceptors.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-peeringdialers.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-proxyconfigurations.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-proxydefaults.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-referencegrants-external.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-registrations.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-routeauthfilters.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-routeretryfilters.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-routetimeoutfilters.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-samenessgroups.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-servicedefaults.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-serviceintentions.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-serviceresolvers.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-servicerouters.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-servicesplitters.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-tcproutes-external.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-tcproutes.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-terminatinggateways.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-tlsroutes-external.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-trafficpermissions.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/crd-udproutes-external.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/create-federation-secret-job.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/create-federation-secret-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/create-federation-secret-role.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/create-federation-secret-rolebinding.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/create-federation-secret-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/datadog-agent-role.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/datadog-agent-rolebinding.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/dns-service.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/enterprise-license-job.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/enterprise-license-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/enterprise-license-role.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/enterprise-license-rolebinding.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/enterprise-license-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/expose-servers-service.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/gateway-cleanup-clusterrole.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/gateway-cleanup-clusterrolebinding.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/gateway-cleanup-job.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/gateway-cleanup-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/gateway-cleanup-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/gateway-resources-clusterrole.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/gateway-resources-clusterrolebinding.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/gateway-resources-configmap.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/gateway-resources-job.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/gateway-resources-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/gateway-resources-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/gossip-encryption-autogenerate-job.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/gossip-encryption-autogenerate-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/gossip-encryption-autogenerate-role.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/gossip-encryption-autogenerate-rolebinding.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/gossip-encryption-autogenerate-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/ingress-gateways-deployment.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/ingress-gateways-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/ingress-gateways-role.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/ingress-gateways-rolebinding.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/ingress-gateways-service.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/ingress-gateways-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/mesh-gateway-clusterrole.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/mesh-gateway-clusterrolebinding.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/mesh-gateway-deployment.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/mesh-gateway-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/mesh-gateway-service.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/mesh-gateway-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/partition-init-job.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/partition-init-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/partition-init-role.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/partition-init-rolebinding.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/partition-init-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/partition-name-configmap.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/prometheus.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/server-acl-init-cleanup-job.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/server-acl-init-cleanup-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/server-acl-init-cleanup-role.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/server-acl-init-cleanup-rolebinding.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/server-acl-init-cleanup-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/server-acl-init-job.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/server-acl-init-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/server-acl-init-role.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/server-acl-init-rolebinding.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/server-acl-init-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/server-clusterrole.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/server-clusterrolebinding.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/server-config-configmap.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/server-disruptionbudget.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/server-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/server-role.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/server-rolebinding.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/server-securitycontextconstraints.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/server-service.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/server-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/server-snapshot-agent-configmap.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/server-statefulset.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/server-tmp-extra-config-configmap.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/sync-catalog-clusterrole.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/sync-catalog-clusterrolebinding.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/sync-catalog-deployment.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/sync-catalog-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/sync-catalog-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/telemetry-collector-configmap.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/telemetry-collector-deployment.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/telemetry-collector-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/telemetry-collector-role.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/telemetry-collector-rolebinding.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/telemetry-collector-service.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/telemetry-collector-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/telemetry-collector-v2-deployment.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/terminating-gateways-deployment.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/terminating-gateways-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/terminating-gateways-role.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/terminating-gateways-rolebinding.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/terminating-gateways-service.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/terminating-gateways-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/tests/test-runner.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/tls-init-cleanup-job.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/tls-init-cleanup-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/tls-init-cleanup-role.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/tls-init-cleanup-rolebinding.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/tls-init-cleanup-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/tls-init-job.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/tls-init-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/tls-init-role.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/tls-init-rolebinding.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/tls-init-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/ui-ingress.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/ui-service.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/webhook-cert-manager-clusterrole.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/webhook-cert-manager-clusterrolebinding.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/webhook-cert-manager-configmap.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/webhook-cert-manager-deployment.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/webhook-cert-manager-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/1.5.3/templates/webhook-cert-manager-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/1.5.3/values.yaml create mode 100644 charts/kuma/kuma/2.8.3/.helmdocsignore create mode 100644 charts/kuma/kuma/2.8.3/.helmignore create mode 100644 charts/kuma/kuma/2.8.3/Chart.yaml create mode 100644 charts/kuma/kuma/2.8.3/README.md create mode 100644 charts/kuma/kuma/2.8.3/README.md.gotmpl create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_circuitbreakers.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_containerpatches.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_dataplaneinsights.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_dataplanes.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_externalservices.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_faultinjections.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_healthchecks.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_hostnamegenerators.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_meshaccesslogs.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_meshcircuitbreakers.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_meshes.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_meshexternalservices.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_meshfaultinjections.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_meshgatewayinstances.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_meshgatewayroutes.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_meshgateways.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_meshhealthchecks.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_meshhttproutes.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_meshinsights.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_meshloadbalancingstrategies.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_meshmetrics.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_meshpassthroughs.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_meshproxypatches.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_meshratelimits.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_meshretries.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_meshservices.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_meshtcproutes.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_meshtimeouts.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_meshtraces.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_meshtrafficpermissions.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_proxytemplates.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_ratelimits.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_retries.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_serviceinsights.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_timeouts.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_trafficlogs.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_trafficpermissions.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_trafficroutes.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_traffictraces.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_virtualoutbounds.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_zoneegresses.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_zoneegressinsights.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_zoneingresses.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_zoneingressinsights.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_zoneinsights.yaml create mode 100644 charts/kuma/kuma/2.8.3/crds/kuma.io_zones.yaml create mode 100644 charts/kuma/kuma/2.8.3/templates/NOTES.txt create mode 100644 charts/kuma/kuma/2.8.3/templates/_helpers.tpl create mode 100644 charts/kuma/kuma/2.8.3/templates/cni-configmap.yaml create mode 100644 charts/kuma/kuma/2.8.3/templates/cni-daemonset.yaml create mode 100644 charts/kuma/kuma/2.8.3/templates/cni-rbac.yaml create mode 100644 charts/kuma/kuma/2.8.3/templates/cp-configmap.yaml create mode 100644 charts/kuma/kuma/2.8.3/templates/cp-deployment.yaml create mode 100644 charts/kuma/kuma/2.8.3/templates/cp-global-sync-service.yaml create mode 100644 charts/kuma/kuma/2.8.3/templates/cp-hpa.yaml create mode 100644 charts/kuma/kuma/2.8.3/templates/cp-ingress.yaml create mode 100644 charts/kuma/kuma/2.8.3/templates/cp-kds-global-server-secret.yaml create mode 100644 charts/kuma/kuma/2.8.3/templates/cp-kds-zone-client-tls-secret.yaml create mode 100644 charts/kuma/kuma/2.8.3/templates/cp-pdb.yaml create mode 100644 charts/kuma/kuma/2.8.3/templates/cp-rbac.yaml create mode 100644 charts/kuma/kuma/2.8.3/templates/cp-service.yaml create mode 100644 charts/kuma/kuma/2.8.3/templates/cp-webhooks-and-secrets.yaml create mode 100644 charts/kuma/kuma/2.8.3/templates/egress-deployment.yaml create mode 100644 charts/kuma/kuma/2.8.3/templates/egress-hpa.yaml create mode 100644 charts/kuma/kuma/2.8.3/templates/egress-pdb.yaml create mode 100644 charts/kuma/kuma/2.8.3/templates/egress-rbac.yaml create mode 100644 charts/kuma/kuma/2.8.3/templates/egress-service.yaml create mode 100644 charts/kuma/kuma/2.8.3/templates/gateway-class.yaml create mode 100644 charts/kuma/kuma/2.8.3/templates/ingress-deployment.yaml create mode 100644 charts/kuma/kuma/2.8.3/templates/ingress-hpa.yaml create mode 100644 charts/kuma/kuma/2.8.3/templates/ingress-pdb.yaml create mode 100644 charts/kuma/kuma/2.8.3/templates/ingress-rbac.yaml create mode 100644 charts/kuma/kuma/2.8.3/templates/ingress-service.yaml create mode 100644 charts/kuma/kuma/2.8.3/templates/post-delete-cleanup-ebpf-job.yaml create mode 100644 charts/kuma/kuma/2.8.3/templates/pre-delete-webhooks.yaml create mode 100644 charts/kuma/kuma/2.8.3/templates/pre-install-patch-namespace-job.yaml create mode 100644 charts/kuma/kuma/2.8.3/templates/pre-upgrade-install-crds-job.yaml create mode 100644 charts/kuma/kuma/2.8.3/values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/.helmignore create mode 100644 charts/new-relic/nri-bundle/5.0.91/Chart.lock create mode 100644 charts/new-relic/nri-bundle/5.0.91/Chart.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/README.md create mode 100644 charts/new-relic/nri-bundle/5.0.91/README.md.gotmpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/app-readme.md create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/.helmignore create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/Chart.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/README.md create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/README.md.gotmpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/NOTES.txt create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/_helpers.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/certmanager.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/deployment.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/instrumentation-crd.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/leader-election-rbac.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/manager-rbac.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/mutating-webhook-configuration.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/newrelic_license_secret.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/proxy-rbac.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/reader-rbac.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/selfsigned-issuer.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/service.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/validating-webhook-configuration.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/webhook-service.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/.helmignore create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/Chart.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/README.md create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/NOTES.txt create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/_helpers.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/ciliumnetworkpolicy.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/clusterrolebinding.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/crs-configmap.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/deployment.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/extra-manifests.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/kubeconfig-secret.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/networkpolicy.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/pdb.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/podsecuritypolicy.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/psp-clusterrole.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/psp-clusterrolebinding.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/rbac-configmap.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/role.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/rolebinding.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/service.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/serviceaccount.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/servicemonitor.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/stsdiscovery-role.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/stsdiscovery-rolebinding.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/verticalpodautoscaler.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/.helmignore create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/Chart.lock create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/Chart.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/README.md create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/README.md.gotmpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/.helmignore create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/Chart.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/DEVELOPERS.md create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/README.md create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_affinity.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_agent-config.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_cluster.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_custom-attributes.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_dnsconfig.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_fedramp.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_hostnetwork.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_images.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_insights.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_insights_secret.yaml.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_labels.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_license.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_license_secret.yaml.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_low-data-mode.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_naming.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_nodeselector.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_priority-class-name.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_privileged.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_proxy.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_region.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_security-context.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_serviceaccount.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_staging.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_tolerations.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_userkey.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_userkey_secret.yaml.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_verbose-log.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/ci/test-values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/NOTES.txt create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/_helpers.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/clusterrole.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/clusterrolebinding.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/job-createSecret.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/job-patchWebhook.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/psp.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/role.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/rolebinding.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/serviceaccount.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/mutatingWebhookConfiguration.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/cert-manager.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/clusterrole.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/clusterrolebinding.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/configmap.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/deployment.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/secret.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/service.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/serviceaccount.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/tests/deployment_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/tests/job_patch_psp_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/tests/job_serviceaccount_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/tests/rbac_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/.helmignore create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/Chart.lock create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/Chart.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/README.md create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/README.md.gotmpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/.helmignore create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/Chart.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/DEVELOPERS.md create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/README.md create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_affinity.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_agent-config.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_cluster.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_custom-attributes.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_dnsconfig.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_fedramp.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_hostnetwork.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_images.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_insights.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_insights_secret.yaml.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_labels.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_license.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_license_secret.yaml.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_low-data-mode.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_naming.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_nodeselector.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_priority-class-name.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_privileged.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_proxy.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_security-context.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_serviceaccount.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_staging.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_tolerations.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_verbose-log.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/ci/test-cplane-kind-deployment-values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/ci/test-values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/NOTES.txt create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/_helpers.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/_helpers_compatibility.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/clusterrole.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/clusterrolebinding.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/_affinity_helper.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/_agent-config_helper.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/_host_network.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/_naming.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/_rbac.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/_tolerations_helper.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/agent-configmap.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/clusterrole.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/clusterrolebinding.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/daemonset.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/rolebinding.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/scraper-configmap.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/serviceaccount.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/_affinity_helper.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/_agent-config_helper.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/_host_network.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/_naming.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/_tolerations_helper.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/agent-configmap.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/deployment.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/scraper-configmap.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/_affinity_helper.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/_agent-config_helper.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/_host_network.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/_naming.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/_security_context_helper.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/_tolerations_helper.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/agent-configmap.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/daemonset.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/integrations-configmap.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/scraper-configmap.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/podsecuritypolicy.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/secret.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/serviceaccount.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/.helmignore create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/Chart.lock create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/Chart.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/README.md create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/README.md.gotmpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/.helmignore create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/Chart.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/DEVELOPERS.md create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/README.md create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_affinity.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_agent-config.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_cluster.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_custom-attributes.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_dnsconfig.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_fedramp.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_hostnetwork.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_images.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_insights.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_insights_secret.yaml.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_labels.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_license.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_license_secret.yaml.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_low-data-mode.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_naming.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_nodeselector.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_priority-class-name.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_privileged.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_proxy.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_region.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_security-context.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_serviceaccount.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_staging.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_tolerations.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_userkey.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_userkey_secret.yaml.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_verbose-log.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/ci/test-values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/_helpers.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/adapter-clusterrolebinding.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/adapter-rolebinding.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/apiservice.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/clusterrole.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/clusterrolebinding.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/job-createSecret.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/job-patchAPIService.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/psp.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/role.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/rolebinding.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/serviceaccount.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/configmap.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/deployment.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/hpa-clusterrole.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/hpa-clusterrolebinding.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/secret.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/service.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/serviceaccount.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/apiservice_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/common_extra_naming_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/configmap_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/deployment_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/hpa_clusterrolebinding_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/job_patch_cluster_rolebinding_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/job_patch_clusterrole_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/job_patch_common_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/job_patch_job_createsecret_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/job_patch_job_patchapiservice_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/job_serviceaccount_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/rbac_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/Chart.lock create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/Chart.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/README.md create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/.helmignore create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/Chart.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/DEVELOPERS.md create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/README.md create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_affinity.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_agent-config.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_cluster.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_custom-attributes.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_dnsconfig.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_fedramp.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_hostnetwork.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_images.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_insights.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_insights_secret.yaml.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_labels.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_license.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_license_secret.yaml.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_low-data-mode.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_naming.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_nodeselector.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_priority-class-name.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_privileged.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_proxy.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_security-context.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_serviceaccount.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_staging.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_tolerations.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_verbose-log.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/ci/test-enable-windows-values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/ci/test-lowdatamode-values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/ci/test-override-global-lowdatamode.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/ci/test-staging-values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/ci/test-with-empty-global.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/ci/test-with-empty-values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/fluent-bit-and-plugin-metrics-dashboard-template.json create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/NOTES.txt create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/_helpers.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/clusterrole.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/clusterrolebinding.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/configmap.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/daemonset-windows.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/daemonset.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/persistentvolume.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/podsecuritypolicy.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/secret.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/serviceaccount.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/cri_parser_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/dns_config_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/endpoint_region_selection_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/fluentbit_k8logging_exclude_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/fluentbit_persistence_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/fluentbit_pod_label_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/fluentbit_sendmetrics_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/host_network_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/images_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/linux_volume_mount_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/rbac_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/Chart.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/README.md create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/ci/test-values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/templates/NOTES.txt create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/templates/_helpers.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/templates/configmap.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/templates/job.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/templates/secret.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/tests/configmap.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/tests/jobs.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/.helmignore create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/Chart.lock create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/Chart.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/README.md create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/README.md.gotmpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/.helmignore create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/Chart.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/DEVELOPERS.md create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/README.md create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_affinity.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_agent-config.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_cluster.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_custom-attributes.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_dnsconfig.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_fedramp.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_hostnetwork.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_images.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_insights.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_insights_secret.yaml.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_labels.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_license.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_license_secret.yaml.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_low-data-mode.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_naming.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_nodeselector.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_priority-class-name.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_privileged.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_proxy.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_security-context.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_serviceaccount.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_staging.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_tolerations.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_verbose-log.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/ci/test-values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/static/lowdatamodedefaults.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/static/metrictyperelabeldefaults.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/templates/_helpers.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/templates/clusterrole.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/templates/clusterrolebinding.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/templates/configmap.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/templates/secret.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/templates/serviceaccount.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/templates/statefulset.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/tests/configmap_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/tests/configurator_image_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/tests/integration_filters_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/tests/lowdatamode_configmap_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/Chart.lock create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/Chart.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/README.md create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/README.md.gotmpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/.helmignore create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/Chart.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/DEVELOPERS.md create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/README.md create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_affinity.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_agent-config.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_cluster.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_custom-attributes.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_dnsconfig.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_fedramp.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_hostnetwork.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_images.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_insights.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_insights_secret.yaml.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_labels.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_license.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_license_secret.yaml.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_low-data-mode.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_naming.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_nodeselector.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_priority-class-name.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_privileged.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_proxy.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_region.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_security-context.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_serviceaccount.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_staging.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_tolerations.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_userkey.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_userkey_secret.yaml.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_verbose-log.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/ci/test-bare-minimum-values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/ci/test-custom-attributes-as-map.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/ci/test-custom-attributes-as-string.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/ci/test-values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/NOTES.txt create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/_helpers.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/_helpers_compatibility.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/agent-configmap.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/clusterrole.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/clusterrolebinding.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/configmap.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/deployment.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/secret.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/serviceaccount.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/tests/agent_configmap_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/tests/configmap_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/tests/deployment_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/tests/images_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/tests/security_context_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/.helmignore create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/Chart.lock create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/Chart.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/README.md create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/README.md.gotmpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/.helmignore create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/Chart.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/DEVELOPERS.md create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/README.md create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_affinity.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_agent-config.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_cluster.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_custom-attributes.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_dnsconfig.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_fedramp.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_hostnetwork.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_images.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_insights.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_insights_secret.yaml.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_labels.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_license.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_license_secret.yaml.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_low-data-mode.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_naming.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_nodeselector.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_priority-class-name.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_privileged.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_proxy.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_region.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_security-context.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_serviceaccount.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_staging.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_tolerations.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_userkey.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_userkey_secret.yaml.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_verbose-log.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/ci/test-values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/NOTES.txt create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/_helpers.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/clusterrole.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/clusterrolebinding.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/job-createSecret.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/job-patchWebhook.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/psp.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/role.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/rolebinding.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/serviceaccount.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/mutatingWebhookConfiguration.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/cert-manager.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/deployment.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/service.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/tests/cluster_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/tests/job_serviceaccount_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/tests/rbac_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/tests/volume_mounts_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/.helmignore create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/Chart.lock create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/Chart.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/README.md create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/README.md.gotmpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/.helmignore create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/Chart.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/DEVELOPERS.md create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/README.md create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_affinity.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_agent-config.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_cluster.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_custom-attributes.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_dnsconfig.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_fedramp.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_hostnetwork.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_images.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_insights.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_insights_secret.yaml.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_labels.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_license.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_license_secret.yaml.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_low-data-mode.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_naming.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_nodeselector.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_priority-class-name.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_privileged.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_proxy.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_security-context.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_serviceaccount.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_staging.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_tolerations.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_verbose-log.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/ci/test-lowdatamode-values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/ci/test-override-global-lowdatamode.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/ci/test-values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/static/lowdatamodedefaults.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/templates/_helpers.tpl create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/templates/clusterrole.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/templates/clusterrolebinding.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/templates/configmap.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/templates/deployment.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/templates/secret.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/templates/serviceaccount.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/tests/configmap_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/tests/deployment_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/tests/labels_test.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/Chart.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/crds/olm_crd.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/crds/vizier_crd.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/templates/00_olm.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/templates/01_px_olm.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/templates/02_catalog.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/templates/03_subscription.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/templates/04_vizier.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/templates/deleter.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/templates/deleter_role.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/ci/test-values.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/questions.yaml create mode 100644 charts/new-relic/nri-bundle/5.0.91/values.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.342/.helmignore create mode 100644 charts/speedscale/speedscale-operator/2.2.342/Chart.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.342/LICENSE create mode 100644 charts/speedscale/speedscale-operator/2.2.342/README.md create mode 100644 charts/speedscale/speedscale-operator/2.2.342/app-readme.md create mode 100644 charts/speedscale/speedscale-operator/2.2.342/questions.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.342/templates/NOTES.txt create mode 100644 charts/speedscale/speedscale-operator/2.2.342/templates/admission.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.342/templates/configmap.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.342/templates/crds/trafficreplays.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.342/templates/deployments.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.342/templates/hooks.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.342/templates/rbac.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.342/templates/secrets.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.342/templates/services.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.342/templates/tls.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.342/values.yaml create mode 100644 charts/traefik/traefik/31.0.0/.helmignore create mode 100644 charts/traefik/traefik/31.0.0/Changelog.md create mode 100644 charts/traefik/traefik/31.0.0/Chart.yaml create mode 100644 charts/traefik/traefik/31.0.0/EXAMPLES.md create mode 100644 charts/traefik/traefik/31.0.0/Guidelines.md create mode 100644 charts/traefik/traefik/31.0.0/LICENSE create mode 100644 charts/traefik/traefik/31.0.0/README.md create mode 100644 charts/traefik/traefik/31.0.0/VALUES.md create mode 100644 charts/traefik/traefik/31.0.0/app-readme.md create mode 100644 charts/traefik/traefik/31.0.0/crds/gateway-standard-install-v1.1.0.yaml create mode 100644 charts/traefik/traefik/31.0.0/crds/hub.traefik.io_accesscontrolpolicies.yaml create mode 100644 charts/traefik/traefik/31.0.0/crds/hub.traefik.io_apiaccesses.yaml create mode 100644 charts/traefik/traefik/31.0.0/crds/hub.traefik.io_apiportals.yaml create mode 100644 charts/traefik/traefik/31.0.0/crds/hub.traefik.io_apiratelimits.yaml create mode 100644 charts/traefik/traefik/31.0.0/crds/hub.traefik.io_apis.yaml create mode 100644 charts/traefik/traefik/31.0.0/crds/hub.traefik.io_apiversions.yaml create mode 100644 charts/traefik/traefik/31.0.0/crds/traefik.io_ingressroutes.yaml create mode 100644 charts/traefik/traefik/31.0.0/crds/traefik.io_ingressroutetcps.yaml create mode 100644 charts/traefik/traefik/31.0.0/crds/traefik.io_ingressrouteudps.yaml create mode 100644 charts/traefik/traefik/31.0.0/crds/traefik.io_middlewares.yaml create mode 100644 charts/traefik/traefik/31.0.0/crds/traefik.io_middlewaretcps.yaml create mode 100644 charts/traefik/traefik/31.0.0/crds/traefik.io_serverstransports.yaml create mode 100644 charts/traefik/traefik/31.0.0/crds/traefik.io_serverstransporttcps.yaml create mode 100644 charts/traefik/traefik/31.0.0/crds/traefik.io_tlsoptions.yaml create mode 100644 charts/traefik/traefik/31.0.0/crds/traefik.io_tlsstores.yaml create mode 100644 charts/traefik/traefik/31.0.0/crds/traefik.io_traefikservices.yaml create mode 100644 charts/traefik/traefik/31.0.0/templates/NOTES.txt create mode 100644 charts/traefik/traefik/31.0.0/templates/_helpers.tpl create mode 100644 charts/traefik/traefik/31.0.0/templates/_podtemplate.tpl create mode 100644 charts/traefik/traefik/31.0.0/templates/_service-metrics.tpl create mode 100644 charts/traefik/traefik/31.0.0/templates/_service.tpl create mode 100644 charts/traefik/traefik/31.0.0/templates/daemonset.yaml create mode 100644 charts/traefik/traefik/31.0.0/templates/deployment.yaml create mode 100644 charts/traefik/traefik/31.0.0/templates/extra-objects.yaml create mode 100644 charts/traefik/traefik/31.0.0/templates/gateway.yaml create mode 100644 charts/traefik/traefik/31.0.0/templates/gatewayclass.yaml create mode 100644 charts/traefik/traefik/31.0.0/templates/hpa.yaml create mode 100644 charts/traefik/traefik/31.0.0/templates/hub-admission-controller.yaml create mode 100644 charts/traefik/traefik/31.0.0/templates/hub-apiportal.yaml create mode 100644 charts/traefik/traefik/31.0.0/templates/ingressclass.yaml create mode 100644 charts/traefik/traefik/31.0.0/templates/ingressroute.yaml create mode 100644 charts/traefik/traefik/31.0.0/templates/poddisruptionbudget.yaml create mode 100644 charts/traefik/traefik/31.0.0/templates/prometheusrules.yaml create mode 100644 charts/traefik/traefik/31.0.0/templates/provider-file-cm.yaml create mode 100644 charts/traefik/traefik/31.0.0/templates/pvc.yaml create mode 100644 charts/traefik/traefik/31.0.0/templates/rbac/clusterrole.yaml create mode 100644 charts/traefik/traefik/31.0.0/templates/rbac/clusterrolebinding.yaml create mode 100644 charts/traefik/traefik/31.0.0/templates/rbac/podsecuritypolicy.yaml create mode 100644 charts/traefik/traefik/31.0.0/templates/rbac/role.yaml create mode 100644 charts/traefik/traefik/31.0.0/templates/rbac/rolebinding.yaml create mode 100644 charts/traefik/traefik/31.0.0/templates/rbac/serviceaccount.yaml create mode 100644 charts/traefik/traefik/31.0.0/templates/requirements.yaml create mode 100644 charts/traefik/traefik/31.0.0/templates/service-metrics.yaml create mode 100644 charts/traefik/traefik/31.0.0/templates/service.yaml create mode 100644 charts/traefik/traefik/31.0.0/templates/servicemonitor.yaml create mode 100644 charts/traefik/traefik/31.0.0/templates/tlsoption.yaml create mode 100644 charts/traefik/traefik/31.0.0/templates/tlsstore.yaml create mode 100644 charts/traefik/traefik/31.0.0/values.yaml diff --git a/assets/hashicorp/consul-1.5.3.tgz b/assets/hashicorp/consul-1.5.3.tgz new file mode 100644 index 0000000000000000000000000000000000000000..047df74481db4e270c0112657189cb12e83d4700 GIT binary patch literal 238832 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
  • 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

    }PG7=(Z@lZGHbcbi$F>S;yW zFyft-)fs2Rgt_e@o}-+kyvdCbZ6T;PgP?!;uylj0R<)qLtw(CABQLmNG^zgSC!+#@ z**yCSwXN@a!;pS_q|EVbMk59MDN$@ggfj`!I)*rMJ(Wh*P_w|WT`?yDG2U3q1C+kj ztq#Vry+R166eO8V&>0hwU`Ws;=$zq1Shie8d1@VkRgwwRnB2r$3{IeKomo`5j!6mK zI`sU=0m~waj$m*TrQw1*OnQmc7*yB}Eq}BtRCEY3fHZQ(DM6%h#8*9^T)fm&%bknz zx#!VgqEj2jFsIwBSFsftE?H-&Y}|tKjpxAxGQGZR4rB_zeZPZ}j9F<-_m8e#U+R=$ zT4L7vh9p6hMLcaa_&9qBL^F8D#^^>MhY8ptCJyQrq_Qce&?qB$N`;^|M4wCrlSpdC zANREx3;@~5v(26Rf9Adxx(m3gSs*Tgvc05FDYL1PkO ztEAJRSEVQf!iJe@=S=s|w2+PcTLu@zGp@)%twUJos-Ya2z+rtjV%wj8f(i!uE~++5 zACxmDjmZ?gZKFLio<{~{7-!>D&7cC^D~_%S$+Z5`oevG_J#^c2igFV1G$qUiXu0hN zQmD|4+yF#{po;XVBMYe3jH@gAsl%lKZ5fG(5RK-fj&qsil;>2^zml()uMNAm$?-gI z=x6nmI+~}RLgE|)#9JyCw2c&LDyhhicJ8;#Fx>Q#duwp}bwlOanxuPV=G_dvv;`|H zeM`5nx!UxgFo^M(xTHrl$OJLFI43Va4zQ$Wq*$KO<2~~a&FzjXBfSal-@*Fy$6xjX z`>NJQiD$K-C>z)WI0CmR0M4WD5RCi@-LQS`2mL{*p`HhRPff9?wM&v3F-lFr4^y+Q z7oAao$mG#pv;#~9ZgiZV?j-`I=d;6pz9H?s^{eCxyxV-W+u*fpk-0SbavZ$*9!-G4G%*wxZO zi(iL6jD)+^voS;%k;VzE0Yj2|$Uj$16}6rs3d!FvegJSwiUSK57OOez2J^jw90s<$ zEkc`2%`vWayK0?uW0?d<7vgj>eXf`j+u$I0wUvMs4z|v|_;=(zVE*qH-__Mt-@s*z zpOlUDxCd+|is3_<5lkx#s8!@2*VoI5lBL(4f`NhpQ=!itA!g*C5~JyBjNaoC_JUyu zX7S$wvV(=((cylPTWG7m(2c-T02Y$01e}AQJA4cWniK7Fl($7BQ+p1oc)joI<=9VP z^i%>Q(S7o7Y9|B#l5LmDz6cULQ{*5jXA9C41D zpJ9}rl*XPyjH8ur9nxi~a0$l@536@%XI0=1vq~Fd>rQD}ZvZ1<{kt6raeXtC_swtP zoTX8Yv?=WyV}7Hj2fxtK?^Lm`Y0fy0QUVS5D$&a&gJ&yF3VG-m%czYC^vW_pv^ljg z!`!~WTEZa;&SyY*lS;)&!C^(+u}*|yxd`CWvFVDWe0#|hTBiDorQ*NSy>*8E+e}X3 z1tPGpl{mwc{#i+I`bzZgDgBe;DY5dNExBtTI2}<`A40Qvr7xiet&QqtL1F5JS-d$M zishP(c0gin_z?c9Q5Rzd9LHWipSw)~Ly^xM&1p)jBeQEv0s4%UESn&&K4Qiy2J<1OH1Y*^o zngn7t(i%_ZZRETy+_PTs+(k%M)uzAJokdYDj7OyPjSk)pwjEuayq25l$ro+@#fE>S zq{E1rj#X#Dyq$|(8X#hCuriJBVA~=KB;#YSJ7F~7(I*yA2 zQYqDFTDiod>jlplBK?uJ_K$F3YEu0jCY_*OQk&hnI!=q-o1I(7mGL#VS~*j>_%|yj z&s~{Uw5%&Q8``gSN>i$yA-buz5cRhm{C(6Ag2mFuifAJqz{zHEf7_8dC(T&%@x$DA zM-;3pc)ehbLv-{1oyl(OPS!6Ye}aDeB@_I7_ zQys6o{NAfxh*p&I%?!^z&B~oSO+9k5sQizG=-Y|qWe&OUE#OjgOJ92ICbcd`eoaB? zwXH`Nzwv13-Lr1LUc`@^iiG=|8oDdK9^W!wSiypmE44%glO0YNewHBSk%J$4Hz)3I z_z;QXofzL9RNA%#yl^?0_y)Bj`$Ff%^;3vzG?yw))|Z3hY}$%jfBl_ z6~`T)H@BQ{!Vq%ep00Ki_Ef2gnAd-N8F3CkZ_HT~_xA+VAtuW)pT<-i{)r0g7|w60 zTnX6yGi5X*;@w{Qw5Q#EK8h5Ww|aFSit08#hUKeC?aheXWnp4}KeFvXqv1`Y!bp4# zK=PH_hnM&i2;(Ea11I^;9mGp~g|$Zb0-yWc@sc-JQpECm*oA<#+QBXo5#C$-bQ8~&4|AX zp6*onzQ+j

    OrmBg}c_qQ*4aBzO}vtC8t8*_6AKTinOr9aQ|{RN*7O%w07=sGjE z4S1JIycQ77_a-2+pX|jFIi%rd!Y35!6z9za>$=}oRN-1B;;L5ZVTcQR^Q^X#sO{As zdAO@`@K&YZ&tXfz&~*oiB|NP_sWRh@D8LG((gc844Pb_uKF2JLRVtS^@fl~OSfWf z8*zCw03uj$O`JXAl6X;e2G{R9srb*|uDC5r2oKzv3_8_YeJ}o6UEP-##XK|^K)B!| z#I`pBF9zpyWwQx&oNmsge;qZwOB%Yv34=8SFlm>tGB1sG6baEgg}BgWLD9jmTFym8 zHv~jP*Jx*D=M!%QuR)p_5#J4bHp*T_Yio%Ac6ij7dh?jHch_h(lt25BZ={M2B+LTiRze0>a?b zkX4N(cc_zT-xw5Ve^Z%eTsnZ4ro+4bwLK4DGg|Vd&J0*{D#!Uv=Sj}Fgt*!&R4mSS z0BM5kLCx%J#d4QyEjE0qSPG8#&27xp`!1D&^8g3`%`(GW6v$~RMEsad=YDK%coj>z zb6L@BX1F16bCN!Ex8$hpzJQ!LA?95UDQ_A-z}!GraIJoT0hiv0TvFVuf_EVwZ8S64 zc-NZBM^5pia&PL!x7SR1_^5~ooTbTo5YZH$`Tm-sH+sJ=VA|Snh;VE+CiBh@NHd*K zWcv5yS{31Fso#gmP-=)^FWu0>O}Y~h-*~lb-QDW}|M}$rZFz`k+Z33(+0!kkbON=b zLd1DUkLQs30^mBT&-=e=2n@&nzI`dH)_x+;<97vVO_<||ca}jbM}98G5&}?vYEERc zVZL?Y=fHkyV)lNN`EileI77&?(bb;L%8AshEz;$ZyY-ev0L805;`Jj{ z-BF!=)m&!Z{4y#=I+S3Bp6|_0ZApvhA5M1o-gS|kS-hT>#CB{7M)+hX*=n~Dziu0n zGfMQM?U<5Za=RXOt2d3NmnPa7*_>I}q-Tr1%u=)Wr7RcJaN60f#CVRxtT(mW-j(I& z?{VL;M(UOkk$*$du+!evzazkLr{s83mFpF-+h+-0FiDedhxK&ak{EgVt`))L^v0*P z-axCIRh>qz;2-Q9Z%A$d*x6t4I9SB+EseDQYJ;TiV$?@%-l{o35ZFNuwa=#7wb(#n z1do*>;XztFlG+dV9eCC0L0dA`p2iwr^gCa6#$=&r++otqq#wv84B<#pszlzD3-uI% zDBDy5bwU{M3>{8?^MXA1&X>`Oi{*xC9rJ1TGFwkIA_=oIqy)FJZ6Tt3x|0ky=0=rr zSo-=&dyrA-9Xvxda#6(%d&AbFWfGJ?xqTmb%MEhT!yq*`P1qTk4o$p-(}sIzK*w(x z_uPVb-)VX$YC-Arr+pviDum^*o0PC$uqww##F4YA| z(`b1r)?hLgBDN9GT4lop_!gLfsu34NOk5^2@!IfVwlKBMHQQ}S2QW}gY)~TqAPHgj zL|GIG%J6C-46pR?6LpYC>wN)5 zeJ)jXBbj{Qwy~^B^0NmNu9Htg^h@A7p3zP2W0i%tZj+a4#44Z4Y&YDweP*BKzcA7f zmL+6}^&22n)TN7Hkncr90shfqSSTFZW{R~C+F`BG=!6uk(eNN~WuHQ-f64Cq5pV6# zv9~b`%if_o5A*Q!#nWbA>q#QI=Rt4G>5qIsMF*{sje>`Lu&FyC=tX_>akA97=odvJZXPp0Kr3!5ii>_WfocLE6EVk*NGUGZlf}j*)o1yi?k9 zAx*i-XvkJN{@ni(oAC&wr1HM=0ZG6pqvF3eXYjo9Vb+!)e1>~*h`%b+e5`O-d@0DG zZg!$kIFSJAL^El!Auqe zec#ft&Q`*2+VDCywbixGy&VhdZmANx*%5kXY`Hk)B1#-Pk=?irU`$6lgLaw?`?Hl@ z(x)SibE=n_gpC(~nwNFLE?rkBpBBpd5)-!>mVzvJ!Vwb@dT95OQ7AtL)lwppZBJTh z0pZlYmZ&GXrnML!cGVPJ&fBhy(CXB?ZuCA*H3q4v(HIPx~1 zg6Ld4wcLjC`)&=wgA|wxdBC9FMS*+q0U&EGc%&fzoGP3-g=8^U#(dCbB_^fAdbPws z<{Bzdp{SU{8=l6_&F$f!E@hZ)Fs@VN@N@U;DK6@TG%MTLPwbR9Isl-}V|c+T$DaGE zS)8Gy_z?XPUn>F9^-q%eYq}K5O$cGa&SI z6aI5D*Vi|<`}2lxef#sN_MMaT(Bud{3%%so0bcsjx(2$KlSMkLj3;;VwhO|^{_lDi za&cgsmQD_jEeYk)1G|$SoNl%c`cd%_=t;&W7pwF|q?8DNO5sV_QNBij@BPqB`P^b9 z(&bF}pIn!vTu5`e6R_Y!#zOf3KUun`XOOwVp{3|Z25sZsW8jE1zeSb{+v7XRi1mfW zK#ftT6&~S6$f(Oo@aQu^z4!l?A1ozT?|cc_&fu86xL-Sc3njAW?>%HQ+3@fGxr4rM z49|WnPApF_d#Sd3JK$>X1sl+6IY&r&^p2tgklD?ETniv8cTFN_)M`dewXd_qo*Sif=Kag~e$!eN50>TLqTCmRJs zSW8F2P}hF>g5a*SR*OH7nMlq&{_FZ5K)pP_CTqHw?#dHiFNa&J*&iNZtVdrwfge>a z-43NM?eyv%d*waJD&3>C@ctT33TdKW$C=L2L!GC&mP!^Ks(%tLnD%Zi^ri;7THh)f ziyo>icvj91Sk_MN!^fhNPZHBk&2>d5ZuuFLxv$RMCu%Wdk9qm{N{yCB+kAv#1SC#| zj*;LoNC_`yZepP#vW`|UQiGDz&dX^sDP+ER@@(k1QJ9H~A4ObCV66q2(qgx5ytc0z z!ZN!t;BPKUEKtJQScEPSP4IkHpC>oV$-15UuPL=ecxq=b+@-1iY3Wm^nOu>xJCQ<7 zi*$1_BUTM>(^-psm0GG2b6afIgth;*vQS7|{wuZmGT>>s-;47GC_w-~2Ea*FAC8rT zbH|^H)u%{)B>AA8bJ!?0pUY&6c7Paay ztk5XU6Ya39&o5byamK&8CeEI>-8%O;?+wow%|NiKd#GGLOudXTI0NOB^Z zteX6&4`7CVJ79*rdN8ktk$_8vczST9J_r^Fsrw)ep_z3QbZS=nr@)PpQBavR8=OM^ zsZP!P#7)bTnZ$ddgeC7-DDHS^jy7f);<~GqqHUUlZE{FGeo(?1AJpO02kz?qfl$KN zcEo(CBC@k9l9q^TLXn;USgpO2jYwVM{hj!BFptc#Yp}Zc4s&Rj&9QMBwR2pJ6rET{ zi>VOe>XOZS2%1qJ*yO;9*nedL-Gm1Zyg~7XDy6Tea#x-d5DbsuToP<*WBoBJ1SJLy z@)t@d9r9Us!N{0t8?-9eq^gqtWTCgwE;9lJi3U6N8yP-<;wFUk!2Cn<5ZYYzGHDH& z0S&Jmgv_}>xo1r%YK!QdULXb;!G63KMbBk=l;kWx9D+2^qJa;`0mPdPgxIp+2EJy+ z(M=`&UUgBR;dXX`<9zeZE&xSGax>@(;#4FHr;x#fGDuCUQyZxtI={U&=;p@|k1qi* zW3{bjGte|eEu>eLwM@bYO5X6h(8_C~fhzj$fOCx4~7 z16`SkXQfSq&6M!2jI1-F%3>{>lfbEk*|=#)8y)Y^*3qLSnKhON_LN=;-q#HTC2SnSo^!s9}Lz}DHKq%ixx2Y zKORo_dp19O^iTcS_);V2ZzIIe@{B%o3=%Dj`lwJAFKl>MlwNx#Aht}weFR?N`5-Pe$H?OnpX`pLy&ywP3@9Hi+!CN zhlG%2IH~+Tmhm|)+nBHqzE|E#dzE|L^esS>?+@1w(JoE^J=&ZA`8`s`sv>}=8GqU3 zV|8rJzf~(%azhBSe)|?wN)zOn#!*TF{HHbv`gaxK!u}^UJ^KUKGBXMkd?k)%iW)RJ zbI6zWAm4$7WF;p(Dj9OKeYK@)drU2tojdLs5pp@WO84;r;a!si;Zc~ogMfg3yECr~ z$+!}?xp$LjdE89W;?_pMwl6^COthG0KnRx{orvP^r>XQ(QY@8<)q?Cjs<2n~czA~` zl36<=MMdh3zlcTC8)QaBiJ242Aq1Ha2Ln-9Rnx%?T=~ICQU?*AY1|XcLJ+y6^KY? zqCJyV{EPNG{g3ga_Q&1?Cm~%~999Scn+BW2**TCB!qpG=EL}TJnE?ZUS9H-YgE_JM zG!Ow1S~0y1oc2GFYt2-ybaH~tH&|It?r7|qXb7D!_-JNr-5hCF)<;L?X4uQ)~FzgO_m7)$WEPR#SAZUXHqOB z2)nFIg<0FM*E(q9?D`PRV?fO6}@&bx74)ZX%o;HFX;Kb@z|0RP zas9<=Xm`hkBu^{2(T^NwTmkNy`;U*ETHH?k<6UsB32BV}CH#UmY*~l?Pxbnv@dCez ziYgPMUj-amA6ha+T`arZ6yC!uH59E|#tE3$U6aQ|u%57c&1g zrbs2}Yk|h3x$F#5ml2w=_{T&JP=tOctD6O*IT0;<4xx0MC!?FdzF<3I zYmIf4cE`;~Uci3+|0!Lw@#yaEyv(I9v(^g~nEl2qx10xnj1at`C$j2Lif`X=n6rsz(HK zpNKSgMNrW~HfE^~oAvR3B7WlwfJs@t|MVS$D|n6jC9m4A55QK?n8Du&j5W$7Y-&_ zO?QDX-dX!! z9YH$c6f+e^Xo{+&#JigyeYlhNMcbzKVv!bcCsqrwF&JlA44Td}V~as7-0+HMB#*t| z7}=55cx>s^qKNlH*pzB@B}ig7Cl80LQm{n-oI;1t0UZ&Et+I189WvFro_fOK2^lKr zqG1!J=+_n_o_}}LM=znxg(DiqG7I5Rj`=(h_Kq&Bo}?PH?HsJzC!Oc~WCH#2NiR!X z02(TtdtYZl$%KXyG+f!VUuAJ)BbL2@aee=5v&JIh-}RgvYiyQP?Sc2{qu zDn!e2?>#}*eU5%u!*aRdT{s1MI-R+)b7VH~I!k)a+R~)RNZe!O+(doZRMG={^EQ5t zW>PAA*-)PvpH(Q3Wi4&nM|CYLDmC4QSku^J+0>fV52==S4@PDO0DkE36fN8r5Khip z##_KfGzy+)YTJGK=KYd1s?|sNqCiPRWyoQ~K>WH9yRlpgBxX@otZQRR+#K~qvS4pp zbkY)lf;FsaKKw#vUMAHo6(b0unfd0n(>yKBo@qKk;oN9dm9R|ND=XaM3_?2755 zgIcuu4eQ*8Pvh&MNQ)Ys!SPZdZB^Z2Da-bv;^k*e;$9KR3quE@8sZs-m8c=0kOax0 zXR&@21n#CD?wWL_^&~d|c3nIBLv0MY!hR`xg~F6`RNY_s9}Y?vjLgQAqh+=q6%+6d zhV+B+HY~@w0fl%W_uqLJg~VH_LGwp>nK9R$Fipf4L23bb24_W^?Hf9|Bzaw8@pVqXvNIlbtfOCXRdTY{xL;C0uC0Gl!$If>w_@ia* z)l`$_1obp6z1j|2++!hqokGkDjJs}SRR(eB=Fx|KXuJA{+%u^WW77(H2j9@EfnpoQ zbIMdD={(g$w&Yw(u+n}C3g9|hu|;?!RG*a`rE*^CZY%7;q~YGMrM4%#^rzU|&!q|8 z_&{Co1X^7YfFr9ex#9yvTF3E{(W`bHp%6GEDOE&q<~-9O4+L^R!9ODI*>BS}x{@;E zOdDSSHN;kk{287ssm%KO>}IRKkTk}jH2wE*oO7D@Q| zXU*97vh#3?6+oV2|6-7{Y^lx#k5ZR;-3J{gg-6^6nk956qz;wih5K~`pqe*3$0kBe zOu1SWTlksB?Cunqzv1Pe}| zdS772l2Eff!`G;%EN(-y^)82{X6zFo0BCzYuH~-(qabTN_HQK0Y81zL-&AouP+ZwB zhrPqNCDt%2jMFL?kYA7WZU!?V@P)4}>A5&l8}hjRO^dH9$)9NFf6`4}UdOKHjH*YK zIh{ei&_h_U^L3}Haxv$AqhV%QK1rm6&6d@YeNUfw+9=5$`}4i7Q72U=btI!q9exR) zW%**#u#;w>RQZ?>1k91Yb|3@e2JvP&Bh&SmQ$PdMVl-b-;{gTg`v4#w%c{_c#vF)3LsPZd%H%+#C=F8lKYA z>jla6Z;;@=MkJd&9T#=2H7%wkQ$ni~*Es~ywB^^u$ZUk_yTs}mxzBYbxkaY`2Wm`P zDD?wYhWw?@kNwAJ^jO9dwhawB+Ga?f$A_#A_*^E!D7kK4^9jBZ+<8>UE90_1R`Kms5YOFRJM%rQ z0Qw`@BHlp2?8-Tl6h=8{U@gunY}E3CwXwUiz@a$_9McDZ5Gh26RuAyr0KXzL)I#Ct zN9O8svW0a^vXN+~-u&oCbm-KjBxyy$>>rRcf-;^0wS!Df2KzBwrw8Cu++7U(zfMK> zHB8yUE8nk;ZqwT^z?OFD7pCZDqxkp*R*OfGMs2UVimW?Aj3eQYXBwi@(D?Vl zqMBBE@L*gM*DArDCovVs{G=A(Z!iD`Y>np`nrs%$q+Rv2YA=aBB%5&UxOttfny22H zLPC&6ts+i50KAdPj{=wZnwp|`z1z)P5t{fpt-(P~z=LhRROYE!k2m%w@0Udv)++-W zQY&@o!6E)udK}t2((eUovUux09zSS{&RRPwb@?{!c`w%DTf@oiq?OC{B{m>6A+=?^ zxynyISLJ)jOE?_|;A^TkBmM3FqMTtFcPve}0LpIGmfMq>Sk$i21JSlvS7OYZVV6=s z{E2#>-|MPsxOD9OV4n>&^C5R3g&^;tnr3zI@*q0q8_@cpIn&c zKXcJGL+baoSNb5D4{eBL`TSU1p*^p^dYQ#k&YCZLgeR`6-&SLC1bl$8ZU)rc@dl@{ z%+9_-!)`XtM_Cr{ZO{S6_j7piW_3t>utsjswW*!$NqdMo*K5^6Ynz(fdC^Bn&w};d zq$41j@O#)ywP%5YhwI|!gxj198|cH3t~S$OKI3C0%=<(bvV^&}=p?_~l)*|MbFNF& zwz4#punQ4ohCNhAjzn1}t7Ru8YhzWb)x1*tS`>6U-_;$X?7?T22NYDwTVA5tH((9v zIQn52Nn5g-zxxkNQoE7UkO*$Mt)52A)w*h=IVy-CUQ2Lebgxtm>qK(Kov7Vco z-W~l#Is{FN8moM^%qh1>y=f<(t@#Q_!xtC}CM41f*wT11+%G8UMsYoYkOiJ+U)q(H zhaddW{dXHDL-niot@-Daoh>6Nof9i!lzL#{Su6XMX`(lUR&-T$Y8;fJILsA7 zQp=}xmMWC_pYirp@Y(zgkN%Q3pm_!@1s-xN($V=+6jyU*g2o6KJWlNJejKGR5W<9a z`CjHf3VGHRLwj6qc)S|$jT>H&{ng&T8$&7?8#cuL{c!uf|29*!vy=O{NuYzz!TmbE zJ$pEr>E?dFKbxDgg^S*0;9NbE(9|Fxp7wRDeNuZG@+Oqd^57Io;5#SW6}4gqmaPvf z!^{=)S0%&Qi%fQAvQ9e77?{bGV&kL;JUk1^W2Qp|FMlKIp>0e#luRHr)R7kB%|>RG zk34G36_;{2-GuXYr04MBI6 zfsE-?%`-rlo%!9L03DA&Jvt~h^<1R&qvZ`Q739L7ZIX#ds+&t5!`HBMt2&dCHUty_ z8s(NvasAGuiRGF7K2`L%p4tNuJI9Q`kXwHOp|Q;>s*UVc zZ*m^1RB}gWm*v`~`~MGq4gTZ%{N2X_lnB`G+xavvL(6o zI^=EFFLx;p{faH)C|?cKEq;th(>I1jR{nBpPjh$_l$z0^u_||Jg%KKb%;Pb)X(`~oUyed z7ci?P;XLW}5rKQt@8-k2NfL5Q#OGLT(T-r2lOpxc2um-w_BkR;L*+W_80&bKzL!5A zR82lz-AE*(00w__Z2SbX`B0^?j&`%;VZ3ho9nE>g^aJKn3X(Tf=;=}H^l);yb|9!g zp$|!}%r^6SuqMDD?E%|V%u!`D-nl&qnc6h9gXAdC@#r+IqoZ*+HbrLKSM^M{4!#vYuv0*2Uf(vf6ZRd9^& z(`*iS;a$K9&+XY_UiFtm{=wVHQ8j6fcNIHV#1A7#4HmE)e%Y_jOE~eP)t(wOaetZB zZks%EMvCAjZ)({S{rz@{?PaY-{Ge4@1X6Q3{r=atgMzd;U*0)G;8rp}_lRqQ+5TR) zko!#LQ9eEU=aH9^uDb)f4g%LTT2YZhDG2QT=k@zD&OowThUCVn>P%V^=~`zcXA4`> zHrf#-*R8(Sv3LhD7?H7ad-E&$51>%7gwU6&8UR`+6@CI-W-&kul$d9CeTMiZ?OXt; zh~9nYy~A3=3P^d6&icr^0KZVQOhnu0Y7~9rUtFEAd==yfd8aE!;v*y;tW=osy1-fx_Zv7$kw*~ zFNcV2n*fa?VwjpL7g!CfM5pR1F6PWo(}Tt$O0W%X(AZeptSWD)SVhy$xZ`^&RZRQm zhLHQ9xKTCrQDnm(&xfFl5PPBWV8QsS`lf3{;Ya=VRW+lFIOU1k(XAk@D$tcm&O8>^ z2>uLIFAV~)zaSjFOO!4QY2s6DwBn=Ct+a)rU)&l z?W#?oS7^@K^kB~=%rFmRKSI9{l|Z0UpU_Laisv+m)eKU8n_d4*x|$9-jo-wcLXFlW zj8d5DTyR&|39PpfyeE;)C>TOdC7JsKTE6ka`tkXn-%HB$J&5~FwtF0_;nog$M^A>*xglcqL5A9OZn!neF<&;PkG>W`U ztJ0d>RZx`qf#yK2J)R#ML2*nwxqNgPq;oIGM&bRAcr2+-ZxOn-M^XPQNQ>H`aTJ>s z98JHvP6lNw^lhIz%<2p&HHOL#=xo$Q3@X&j+8J=IUDs2t;(A!Tlv!pG#HrQ}lv;@P zJmfM=b^OU0#~~`48y>!|FGWH|w_RH#l`wkv>IxpOMJzJsMcCXtiYlq+!vO6Db=Eka zBvH*H1PT^#cvIFW4R$-qr3TT9MBXwJVZfp1 z(#^rgS7ZrBD0GTPiuRC4eE$s7mZMrLz;?+|DBB)0aln?EI)>ML)(IWii$lwlNjOki zs|dJ4J=)mGPmpEGuMtKmB3(jJoZc4<6Er)Rnm(T>;rcPibuwN!ut2kU zvZ~B)mqxMFMAmO?aL zUcIIC7nbU&ARB|vD9=Lo!)!3|(cC-yP1IzGKZ(JA5-Ga%6Gs=Vw~cSljP_k1({u#R z5a=a5*;;Sv!0u~BX8?Pg+z_4I5FR53UeVpQK>)KseE#U!Ts_m9;anit2KWVm6G!0B z0QYQeY*`sX@}9EyJM8E{`>aKS2k3%LNakh3`uw>(Lg4-_=hoGY1nByQAxy|A6xR56 z5`H3G6EKu&RAA%o)5yPjtvc!dk+MF57aGLq0r60MHCCkcJL>DC{k)gM#qe>bw~bUN z$dH@K)r|fTC$HO@V4*CX_e*(SSM(xa%v;RCUnd_NbvgE(bGlBWWbiM;aV2cK2asfI zM6^L+=>$$#{U=^qt2|r}oRIrFepi>h#NjxpgK$#&=-TbKc#l9OD}+5npF;581L(5Z z+&q*AeAwP^mt%8qKYIk@znhZ*U%QVB++DnF#iQFouy4KWW&ys{YJvt^5ued76^mB* z?FF3k0k|i9s#5r1hYkij>aS~jywCaouT=rwPeB5_$!>TsFM7PdexAeyTN@+_RhdR} zj70I`GLA%(j1=>H01S3yHfrRds7bze@uqazxwWIDG6!d1-Ws`3%SeMTKd}XESDVOg);~ZSC&? z$@sOz)S*y1RRAivFN=c0j@cv@d-1kUhytMk?R1{22p42Vuv9Ny_};l$KMKbkMWavS zVXbg2+iSC8<`Sdffh7eDSW&CluOF0c#}t99*aik9?DH|ii||`*Ibem zxXMxCvLw6}y(QJJwe1es7YY&M#634%mOwXJ$}=WsX9 z1b2l=^eWX_TM3r?&5%%udiFLM1Ca1ua~&=JzttK7tC)0ZNEa7YgNmny8L>g?w~<6O z`MrJ1r|B!I)Coy_=hbojSV#`0jL4~%TaIe>S+|a2>l;@6b0y2Y7O@acAF3U! zJvp(hWXKiMf1c_!IIl^Me=Bh@M=t43G%`TrH^EO6gq}_0-etVuSX=*6g{stWqEO zkYh1bfP74sq(JQ2rQgSHUC3T?`LkAcrZnSv;O$t@>gB9h!#|{1J~2q1DVItvUe(@> zX_w6Rns`qU(qBM|%qil;I-|IsXu;w=GkN2r651Jdk{*laia z*);O|4RqK1VUaZPRtbG6J>{^tc)t4q@S#%XUagbJX=Ebz39kq5uxpKMb0Ar)i;4={ z>UYCx(@$P&)KrPG-Dzb5=-CF{PLyc#SOHI7?K0tDr`^|A3)ce(q)AS%G^@BO$S<$k z&@@Z_Qva~wKRb;sAu1V}tc_Hj!r8%0!3_nljTw;(z8Jp1qn?Qhq-@%zt(Cy6};&qIrRi~w*tL-Kj zu&CYdKRs^F9?{;yj`U9+)2IjLv~HayDT2`9YU&FhzVHz?9(tRSmQx3#0vwU~?FK1= zB=!ZQPaDJ$BYMLFBrXeUWLz#c8N3f>tJimzMdAs0jppJ4gp?h4jpBBP(IgGY2~YY7 z%ySSFy=_a1RmPcDYN>z$b&ig0@U2!BUcvPsPn#*AW1=WgV{)O#%L(^#L95wG%d5W;LXC2fS<)=V}wT z8rB+n+Sm0GA#r+B2VrdQTm-jLAE6r*a=KcuiS6QHf=}EEsH%6QCne2e7k6*hSpza} zptQ@bw->&;<#}T@eCTmv)qR9!0<{bUAz@JFiAIx9TH#c}J!m@ke&7o`X4Ds{@R`%W z1-_a#x2odndzz@_NS~M%3=d(>$#G0UFA>`7I<* zX;dymk3m3O-UAOX!3wwi9k>{mf_5E#&MjZ&H$7LTfVG$&^IRVmAAx&fi!PR5u-@l| zAuc$#I9sl zL0QS|c)G10rYR1uiUe+@+)O`))M?!k8xjT`YSLT_(#lfOgGdJyRz|fVwJ!STDGd3l z(pd|2_12}^S8Ak~feSSShw&iTsY=)q0=Ym0qa}y%JWk%qLkJ<#qJ;V#JTr z)Zu9EKer3|g8YIo~?BH%hW^Ogw!k)Rb@6E4>KfaFMF*=K%!&(Emt z2{1;@wZY#}FRlbP?7LOUi`nAG1jZChu{yx^Bc48vmFXD1qj^iMUagSPT)TK?Elm2* z$^?@LYsW*80_dI{TztV9cA%i)S<55)&8Cf=%sm$6n*e)xFi|vWK=v>T^u!8^D|iUs zm?AZlXx}BOXQv^sUC>VgJACS`#eU#LZG)^_`c=ES?f0qJrZ#%9?PMlo`pC0*Za0FU z&=(ZDe5_b=;b2Z3iZyMvQwU;Gg2iv1m)7AyVDgI=us8Vx1HTM!?IuaRltr{avzt6@ z8htYV2j?Vw{SHrv8y;|rEfavG31$D6Xr?mTeLm!m%A3mYn6km1Q<%hqG9~?i>)C36D-n9% zLYF3uz{V@COB)y>=zgSfSa*sRg`QcP))aoUBZF}bG}@Ei-&1M0^oYq(LYt9RAt3Nv zHR{KI1+GACsOKM*tu$)$TTtx`Bs{oHYA&Xq*O5C}cT+Xj)2}}|re1M1JsasJdps(C zTW=`nDw|i;3HBxlqXOi|*faV_RF4%XC=o%qq5Tx8UXH_WB~OOcmoJ934zt;aS0S0p zlCP$~52=wR#BgFudI5Ea$x!}!%dsN-KqZYL-;DGAV{mCj_)4UvH109fW=U|G0bYT_05LLZvlf&bHw34Uv&pp38U*}>6RZX>o&u2+fAayS9d-o zH$MYu2Z7lR*riVoNmzA9Yo>wd_nF#xW;cZjR8SY%` zuWH+?95J4Sq!PCJ~} zO@YdMy~R_c|_tttQ7ipghsJ=9Vkeu z&1cHU-wA zQ835>&z64QWvyPbKd2#j3l{9~{0$uZFdY|nDh}=>9SZ#L<%gay#)AMkDNJo-6uo{f zi87R*E=OzIfN1Yd(t-NJ)_Z9sJqf z7T&<(BHSGtcMZjC7~NppBM*HfG|Uz{YW^(J5*xMX5&t&Izdi~M>va(+@K#*z8F+|t z+cN9#RFwK?<3T@jK0kX<>gt8MV~lFIN*!ChyfNdB#08(kTN{ zt7JSGl=rhZpJCNeT;VlKT+M#!1&(R^Op~)nMUVX@T?+QWgS0v6rxYa85e?x-$b-|@ zUu4GaKG|u{=?^W(%tr5F!70vx^c`UC-8b*un{0v{^{RfZ$R(a8N?r{xDrx1-dX3Mf z3P$Xygr9fv&`sIAU^tOqK0k)a1N{tkW`s-3_Yn1(>G@+2T>aY7(nm+A`}0&sYojv) zsnU0DxS#N!lH{-HjCJVIK786+IGmh}N%NsjJpLWcjp{3UZHD(GWvtOUS;-p47rEC< zHda}2hTgOWaVIe@=1lzw&9;cDI9D_u0XKdQ{DHZuYmMiC-t7Toooo4(`x;8FXB9ep zo}X^?Je?x6%v+==Kap&;ZRn;AScztB=)~a9 z)9hQM!U`|-H%7uN!ve@!7c%q!hs{oyRmpWgY%Z_4ceEdC%-Z+oBj5KmoY&pL-oZ28 zlTX_0H!mCCrEjw^q*|zSP#=7*p$jHy(eSr@`y%+xSgp1K&HLd(pFb7DwAb1dyH-eJu@VU@+sIS(#BAx ztXLSw7jsbB)Gw#b&i3TiZF9WAI3V-)Bq1y{A#N~vT`;4_=kW|MuCF~ zEo4Fydxzk~+?ok-2}s5rl>_Xw*K6qr|B4$>N=!odb?!x85T@cM0srs&F1PIQ7_hwB zqqhJ(BYM~RWL0+NQQs1wiXlq2U2mRw(FkM2CDX{@N6RJA$5@YPAEQcYlnQ414!Tr> z=~|@pcTAzSi9IC)_+^Lm8u3drRu;(ch_OS31d+sz{;P6t7hpF0&y25$&HW?pz(_c$ z<6`O~XjCP*bLi{5zA~OKf9mkG79Thb)|gOw6hfDAdANJH3fDr$4t{U8YAx8EoR3sj z4(5eetVYTQ8B5)T_qv7~h?6o*x=-70FPG^8r5kSTMl}(tS<}hGsoxGm(B_^^klmjd z$dnVxHOH6%*L|Z^?lHA!zd?i?M{a;x+YQPmO@tKWE5ZlZHtahWihI+jD);V!XV67h76z+CE+Sz6%h{w`Jp z$iV2_hbZmxdcW(2yQs#|m^p|`nff|6r$sG7tHXJEBu**rWpr(bjQXTYc!>4RT>G{$ zebFB?K>c7icEoXF#K4lYA32xV6fD*Ec`jAFeHITK> zU3!)nDz8=_;$8)w2Tq`F!mq)XFpT z8q(~UX_+|$LoD>8Iiy=nUy+2x9cP{~_i>$eGPUX6o&AjDZ5szz9;_ckpT)<>#gy}z zsi6&ca3zft9tkUUId%HGQ`=d-FWRX%Ow%Vx;S&`9s?h@v$ATseq&KB#H^*u^CJzzH zuGpc`;^4GtKWb^_c2Pj`qyEm0p8-M)V0GO-1>`bux#He0l@!w9*h* zfT?CsJ?WO5c5m75NFPwO=U>YBg@s#yd_12gxDa$#?Htl@k5V89jM=E_ z!gYP6IO*Z1PoE%Z)5GG5^zrM%2GhGuQp)4h!z*cb}&D*TyA6YuzANA7DU!| zQ{fW0_}=6aURuI-R?G+)Qjg$x{VW_1R5Z*FY?PiGdJs4GwC~j z15YxRppP21+)GqjdO3wb=v6m?ZGGvhu;WXH2~hVBrYa0TPj+7EhW{~jO6c5zA;j>) z)$*EQ{e)PS-l&F4<#o5R5s@V!)+q_2N$T7p$ReO-GvB_+WYIA=t6no_lFx%pm|2jq z2LY@dn5B;Pbfwj@&OR}vZcZu^ug0!;`Y45kguZC`B27X77!Pei zBI}r*Y0X%y-YE&K)@L*5s0Bs%Q`6BHD$ZDYZy~6#(e6dVsl;=}0YK+fw6?iJ8U%AX zUs%}_?5gs@)b~ecH31~yXJMU81KH`GkO}HYpoWR0ViG1aC+llb;>kBLuw{%nM%Eh} zz~&i)c0zC5m|eYCy(6~_jk5Nj(ed7v)6R3=gs>jXPhtmg3D$j6VN_^4S|rKjqKs;_ zXZ6Xs!(qKU>cLW$j}&9t0+7uz%N{Ec!_pF+-40UI=@i1)2TP_!98_ocZZ$|R4kUKN zzt>bbKu)-2khQZGluUa&U0uwU7zY$Nf-wdW(z%BLXuEWiOfKxp$)Cg6G^DJ&To2@~=R#IJRHF|`2&wLVN(pXO<>8zMC+2FMq0K9!6+IPNtPkiI*zIY+ z%Dij(RWmd^eKqA1-YZWAC%jPOgwrWPq#)6KW@*ux4rYg=Vq9x=E+fgh;mUfvEgm!5 zPH#%GG72ac8rvv)n+WSC7I6~0z|Qtm@b?te1g>SP^y=-yc0DZ$43aK-0*2*I_X{;u zjCV|&`NvKX!=g2`*rUapMYY{#YCyWgh74wdm*rI(%OFEGnLil0;R%-u0g-pm7*Wa= z{chE9f0dn+4=VH|(AhjMgix`rf^Xr6-QsHr7Ty);#A-|jki2#!)3MkDcxHads35gp z7J1@+oybZ2Mh7{czd*C@mVCU#v!Z@T4uugSs?luzv?aQskX^B&04$#;e=&!N9_b|r z3dF2-lVSCv2jN{&WenZRK^H0C@G`i>F+%NlFwmKV8E|8e$ z=p07L+5~KGZylp*o25wT;4~46-27AjusK8}hg8J5m=&t!g|4Rv#oPZmI$HKsT5yJc;1qPF^0p?z)#VqPZS_GWcDOS z7<$nA=!L4NqT;J-bWz~9_N^gd(71ZWwmfA8|CErcC3YZ=iuz@#n7RJCJ-Gv+^!bGlFsK! zqZQ@z=dm!R@C`IYw?6W(?nd2GP@z1_RqWd_PnlBxiDJ|5i>+lyzZ{6=B@1mLZ#L{H zG=R=9nY3g^{oYwR&BE=hw(>Gec5DAqivSp9=|@0=bi^vh3&2qeE%?aDVp}|5hQ=tB z0etyA#JIB%Q<8~QfXKeY5nbN*t(GGY_r$R8A`(nJ`M{FWuXeN(n0?n#NT%l)Uk8Mt zEjy)=JPD&_X8$D8!a;NgOBUr6HJOZ>wYhzU&Z2VLQ5mOq{q@;Hq57Rzs?RyHOGp`l zHZhj5Oc>F`34R8C!1s&D(B+|rz3OxY4a)(o#$(nMDf^WBp#U`T140BxUqR%-zV%ge z+I{6BP(t^^k4_#J@a;(tIE1JCHtm8ear`=fK76+nBPQj!4ve*hvD%}|E<`vE&D$)~ zVEB3y6ZD6$FmT+W>Ol;8Pjoi+23}dMAa7p1Ks+2?*t{?pYA&+G-kUL5yr&bs(8Qf@ zXRnVv$LksE47U!z2Pum9v~|L`9122B8i5AYSf@j8NL4q4^`AvCkG5)vOMx$@V&-gN z6hJx2?-&dJP78G>iN7@bOztNb8Z|x>jVzAT$i}m0J^fHn##8nYDa^0rX(&qZ47w3v zf%rMPq{7qp&A#0(Wz4FTNtYA^tIUb@WsVMacuXs=0u5?HjGYol3EyaMPlJ!25VwS; ze^W?;r89HLThL6;PEKwU8<&i!(YVKYbdbT{buT}?hZN;}yZ51P9}8CT?3i84GV9y6 z+FV6jk++m>$$Ve*|F+yd3?9Ghy3ahdHkAuHzc@3r90Ma3du$H`79AI>@JoCb{{$n7 zV3-)|#r4*!=uYj~ZeiwcJuK=shX;jfu6HXr_RCa5HPkN++U24d0R;ZYbP~Gx1!%T9JxZ{UXi zbb{vN(^rZ=g8{5?1a{b(tbQj55yY1(%Z!u9qv)+r7jcYBPfb5098?Lw+eH-xnBu=| zcoLtTFhR^25SpfkF{h4ZHXo?*ikp5!0*^${!P1g0^nUg3e8lR0&xm@y=YG37$Sk0iSMaC_P=ytEY*O^gTbq@}oDmulehz zG4_Gp?qyj64<-4CZs_?zKaB+#)%~9K>qi@=@mK|`QR#KP{PZ`i(@(k4gUj?+j@MHbj3Y@qGYU&4T2Y|ENhjaQ1$HE5c zZ(D~0Faqmm2;Nt}A1?^VzRI(;hYJuD7%VYK5R#fR8}8S&>fMKDOyhBGsY2l18W--5 zPoVu90oe*36?IN)4&>$1G+TEi!HK2#)Iw%aSz_ss`gpkTDSM(TKwX)9e1}-tpN}Wi z@@X)1rp_MWyL2q7z<1Lau(NeO`}iLMX^2|9dyRSZgW{nIPdT6F zXWu^Xlb=iFq^sDMzG*35w08P8d!}DM9V9!9H;5{~EN^L3=B`GU_>T${ko5K}%(OVk zoBF#n%fnmgT)6FIi&}31Rxi<+#C367OwJBjdiY1b+I%o0F=TEEMz`fF^_WgZoRE*h z+uG8Mb)@C+@7c6s_td0Taz$%eX{Car>~hWc23+!B<)YPua=y*gg-%X2>Ai(|ZfD6; zg_5T1gR*;m($l&A%D5yDYJt9^|UTdqGFQxUE(Ao*0$T-(`9(B z)xw4NEUTLHsPUr>S)0T>3#UbCRslP2WtY+MvNE6Y*klCD^nhz#sPj$b6LmJJ=if!# z-lbvaaj@1FhqQsa`PR-fCla>vnSMUy|7b%@dxFjwAeVnKBk65}) zh;eVQCIg^!fUvUJMWbmMWfv!GCK4f>-HK+=z*3>_obfIc7#e_zmZf9s#w9BMwirpT zBpI4he(6CnK8hb%pD~@HA+Wjs#%-#>aH=Aq3k#Uta`9Uc(JFHyCvCdq=Iw-0e1SMK z8v0_%N0nM6E+rZoAkVL_29XG_C>xV~+6oE`c+A zCGD|VHlIPA_uPy|g1i-7dIW`rb9_Rzx_&MRhK&5RmD(5`8fEx7VV$nPpds5J%0)V& zc9!De5+%5#N&#_AK3Af(g%}r;cfX)a1)#eeTtgKTJAO4DyAc$#D~Sa5s(%Hnjpg|B zq1ifHxWG=>_0LDf9^H?^Wz`YAIg(BoMwmHG7!b*Q79~MJ+!Nodl3Rt9<-x0)9x@5^ z`czzmSChKpzXwe4hy*u4BTWuJ?V`R@>s_Bxv?Lu?>Ea5bj*0lCA#^W z`R~@2UJ_jEmZ#3C*QWlL^)!6_&I!cRV%#4BCs`}Hc0o%dP06>}^#PZJ-%TpL z#l#4gTRReWVNZ>AW(wgc86JM=12MlvJGQ(iALNXwk3eJj)z09?;Y^D3-R1{lbHEp_ z+7wpq+3VqxtjJH4y*;>qF2@7c_$icXc72uvYoO(j+Zp`~IFm$cl75jYsUp+25v&ux zOW)uc`g1^G4zJddKvpEWa~!ZOqO~*X$*ZTqjwSzIFY3!rO(M%YN$#o$f#Km_IT*Jy z8O4?QK7cXLBOOoHe-DP4lWA{QBW`$KRBOM3@1Rn}RS4Z1PkB(+2_9_0#wyr%Fm~*4 z2k1_vf-41dJVYugw5v{2-IL>loKI5L!J9t0F5_`DX7_}dFKc%4oJDmnbcdZ=Z%3@^#S*gJ)&sY^(`wg6Mfq^pjU+Jl3z5H1 zark~{au}K9=V`X3vz3z-+?b$TT$o*UfPFM?`iv6lMjT(BM|o^&%r+A77TnRPDEt!V zJu?y81Dw>~8}3I9WBt<|4GnMol?j7hb{U|kIvs^*dSAC>tphf*mX0+#h4|n#D93;n zmiRRu_OOQ<2j8M8aSb@zjh)w&C`C>;0+z2EFVeh~9OoY)m!#!!Tq)U>%wEMk)A`Jp zoC*$AW6SS)^B_%_BV(sbW<$V64;c(RluG(MCFl3@oQbZu8?#{U*!b#K3WNY?tzEXAX)Mwbgkv&rg--9U8rPZ+SyltZOLKoBoI! z5$PTI+8x*-i-RjWNjf&$Ybr0#hL2cplDCRFWlG+J?`-zx#B4I6mm?ZNPCU3B6qxIg z!C!|U)NCW)5ng~8+Km{&T$>b#ZCqc^Cy|4tvJX;?XE*TrtNy*#osR#25x00rmtk7k!e+0qA_$uqL;X}a|J=Kl(mt# zwMD1YUjRjPH$ahZgCc*=14W#B?OIV3%$Gz4O8`(#n)uduBVh(h+VFd?(<1%WlmV2+ zQ_KP3qoOnUvg#OU&(WxRfl1aPyO2)sMy^LNjC(tcl`w7FvD-IpX=eKLC<^n0~P&?R*iBWNio9w7JzX zIXzfR&U%w=4a?m{+JRAjABcuV_x5CPt{*xp$CeL4hQ}H9v?dXi0osflz)37pg3RT1 zx@+X2q|6A$7~4#4BO0)pvD8>TC*4siIljrINK>leho)1S8;vim)VVa5|MT<52(C4B zlyOH7V+Pv?i#>V!rSFQhhJMr?u7fyckYoxJl1Z?#q zi4LidSMw%n<_{HN8rrpPMQ9zC1wlMrYI_&zZBvp>lPG<5nNf+?YH#2nrS#827H|Kb zwo7*3)JkfZp+BwSy#I^qyGOG28djV7S;KBn&iI;#$qMn8+E~z3vC7nTr{@7K?J=J+ zx)OiH@2C0kLk zI->z&xx~z>p~kAHEP-E=Ae?Zgr7H(LO>!hwdSOgDfX?H~a1WJxORN}NGiw~(L?nvl zI2u(-FZWJ~(h5N$F3SiGl^>yX6FkNe@w?~PcG}7YPKwlVXxRa@XR!nPfaqeVkqNdk zbA6LbEn9aEy&`P3%>-$VAIuG0Ns2O70sLdPx&_KClcgS#yj}WzkCi6Gn{@!Z5mKd} zNmKZ+WHWRSl+a2k2C@u3%>k(d3_l9q$tzAAQEwimR=@zo2Ff% zrbrU2wfgB!R6%>p)H>MO6aXd;G4CVN^EAacOVX1FAh3jl#F&d8)cdvoJP0z|!KjbA zIKC}zhKr9V!_^5c*Tc`DC#PdG(q*I_zQkyKm<=CIP*hi&D)_~Rp+NQ#AcQu~AMXlX zmt&$Bo>P2QJo9IfY(lAOGdKwypZ1T7b4#R&UOs#}cQ!pQxB|B~O7b*(1y^%A77dH7 zOsnj_8rxXQu%f8vK|mj6*Id!jL)?$httJ@W>viPIzod{$Xn71OAR*c_H7XT%t7~#- zo-?vp)1uTmLNZMMkTOb|qz7eP6=Te+@;}F0_A>r9#2PU1{M)_3IMQ5>!6uD%AAGi=wT#ODYk3}bSzGA7sTX4Q5&ysboe_%k5|GfSgIQ6IhbCHTOPpV>m+E~ z`{05$M4l6F2=906d!hIa+88&Q1qA~sLWanqixP6^Vd}ZxtOf54f~S1e+Xr#M{fdM_QwAA$<3Ie6yet+ zRCkl%@Y=E5!ZCYbo#I@xIGqYC1RI9d@5UvdhDcGzoO`j{Mi1&((%2Kkd_Gms(x`Dl zCY~7$-rNRP(w$?6WZ|I*EN%or@~Ke_Ky_5lMyigg2v4+JU8(2Zj?{w)o+so~qni`; zO7|iT-XNgr4`qvfU}|Eccd|6og*15!i0Wc+^xZE(p~iY#OE_jtMa2bJ=KUSsNK8AvrVDW=zvMkwNsIWZs7q zT)`UISiZsf3&N2~ETAKSeG!oQz#&kyPT`r~o6%Ow4V$kU3ghn-4{%t$XZl~Q4E;B_ z7AxHfeJ1G1V;HT(m;4LKVr-3A14;007VITbRk{n^9{a$W@!L!34;cc~oj*R7q7y@8 zxU&#c1pl?}Hl0?D(;VG#I|_l?caZ=;K{8=;DT42F?o=b9b7W%3QJv?{ z?DS)l$Y3~v=!J`kxF9ukn8f1phvf?ts7&O$&hX|Mh*EY$xL2bvHe`z@ns&?XvS+o zpIKiaB=2?SXnh?!plibXA!4hb`QO*R&ZQ`)!}&C_PMNw6??yZ64uK|Ac4<7b`H%H~ z5lShgO#nf_AB}Tbv)DGEy%m?Solb1`dx$)Vc+42$)9K9^S8?X}1US1ukY`iVVB!1F8pzan^zWF-}H0EKF_xFN$*Zsy}z zWJ3+5hkr;AL?APC9sE!8q7M0uFK$%&g3mm_!;+RRd=&H%ZOa{GXiI6hR5)_p&ZXb; zqZnTP9TQr-7PVX(l^o=Y{LEUcRdTUFGO5h-p~B=3D{|b?0Cwfbk|^|(Tro;u%ft*8 z8ai;GKxrAe)MHQ$>H$1mdC)z=1<#9OsK=2*7z{YmM-X78w}|z{mn1By-U&>xRuYeZ zfRdV6)nCLGe6&#-zN)@1NuJo-Nc_H|4K;Zak5oqRC~dAi^;T1V0lBD;PW6$y7A@a( z?`Ro)w4$|zO`tLn+bC2%xm+odQ?GI0S%J^{m2t#SwwI*?a`SzGGZ?9J98t`exfJ&q z9we_{qo#n5S>5B?q_vODA-bzu^X#ky&qn5hzNRzxT0cwu(yeUc7Pn;v(|DA(qIw|) zcWp1|xjL4CIjSnB?)11!YkXyd zhViaTe2Vw(WA@#dy%!3&Me}0aphgq3)>tZ=%YDg-cGJvTUW`R6`@hNGc+)t;#oAL& zsz<phwuzrh%@f58?H1W_Jw4+AVziHG;-F49&;_ z%dP2FPLm~^OrPo8JMVbd$xLGD&q{9g9b|FZuHtSkG^v_#8_LP_7UXtRjECg@wr$ci=)0Ix-2`Ol z{^CTaSTLv5v0#m2&~2ny6afeqioRD>BVpZfmM*0v#hP>q^QlVK<*3BF^7zcs)M)9m zz$&#vyWYPSr+fLgJ7GrDB^NwP+X-=(u;D9{3JHP|hbvEu7S4w`CA+rP2Z7q7@X~nj zy-Or&0d~0qSV0apBvUT18;QHs-oR>aD-HhbLWuELc(yhxm}YBO4yVJHP7;1ceLUIG z*P86hQ%Os-My`3vlHBk9bDpnnc&1%P+t;zkiEJ9b7G`c|sn!NcFQoE{6wJF)Vkd>x zThvGdUJ+CJChLnLaUYE-C}<4BlnaP|HE$4!I1d#}@P?A@wOOR!z@YPN!6F0HaYq3H zPNXa}&NDudbTikkH2iK_L+SeGay$RI+^4*sk0xStmu}FDnKleb+yY>gO2MCf$dFMh zwH71OKEy6Ng7)hG0Qn0GtHfn+shvCoxS2{ky`Z{suna4B=R(1;zSnCvUg$Q!ISal& z!|VKqu_WBlzWhE*h}Qng#aZeks1*Rm(xaFeXw;fCwD!)>2?}E~1{TfLDIhjcAv=hG zWl!gb80G1RhwVVG)`;Cye1=rKn85oF%&Kn~4f_#{CR%uNKn{jlXZ1&8&A!1cI8U}f zw6Yd>p%60(2vq__sqZM^Y6A_nb$voS+hc1hcIn2}6s9xUjdxZI*VCv>=T>yCG8|m6 z!BAFYfYT`on^sKWFeT;X|LsPvX*CIn(oz1l;5YY6G+8K zM8x$HhMY2YI2Vsvzvktrs%o3-|52m}=*ySuLsH6dW7_cTb13FdLky0Mj`_b&fpkb| zILps&s_VCwf92YCWis(1krYy&4I50|#r3Q4phvAhQs^k7gBJ$fevil))$i$`Cs|cd zzG~zO01VepK1#U-zH~qO2S?Xz|Afs2Mj(q6s^#}ZY*Ca?|k*Y z2~I=UH_$u%mv@Kbi|hv5M2rNFEom9W|8ZdCFlD?QYZ(;zV{FSOql*yFx` zhE!=Tm*|G7t#>a><%iGY+V0_yR4JAqkNnz=XB>?U($0_CfZ2wcP~H`uF;BD}d4O#! z(Kr0{7Ezr@D-kRk$t#H_vBmWah$1-K0d$@X5T1okx zo5E6hofq5^d>)C)C={P_)?$~?#<`N)e<~&~DZ~DJ=&-FE=D|RDweGmj)3Z8Piq!2= zLAWQPici^u_?dkS5J4D%9gGRN0(}Zs;s9V=YlfB4*!b~Tc$gM4?X$L+m^HHcD^MHR zqYE7Tq8k+3AIvLHUF&D&&N8gw>fV-V$_w)i=<-er+Y*z7y#=lC=QAby$RP!)IKisK zcQ*6F;6Y*10T&o&W8Am%K$zK=>Ms37r7Bl2IXTo+cp8d9lW!!9;#tYD=l)TFTldEJ z@VM1Wy?om6n9oZsEI=1edh6`_g^i?fKQbO`ON|z>4ycAsq-rsCKN8{L&xZ}CuNTAo z?`sl>c((TZRgZ3{LUd&?yj%A~-&1O4>H-wc~&X_YaHJ>5c2w&?#6(m^gjW)is z+-Lq(Fu|pHuzFWbDgX~sP1VZBwTI6@W?g_hMNWM!8Sj(SYI$3kO(i9mtd>4Smi~~6 zlDAn$X^3Txr@T{E+OQ#?xz<>JfKCxAeLYKQE#5L4HO{Xe zvOd0Q_xIs)J$-E!{wzedSWATeSwxj@l}1>zm|KAuCbcS}x>jr(h@ABq&0HL`mNr9^ zYNIGVrLM5s(|B`Lg4#@0_LGL?{>4S3+IzT`!GRjVXy&}L8yswCl&aWNG$z_#l@}59 zsvv|7a{IwZ;gD1NdCmfQRh87HQEVj((O;4tC7hj@XqeePjg~M(n6Rdt5%q31}l)Z6$uVkeW#Taswc|}`CR5}ooU*mcr_VRw}t9aOp%^=U|;x@3J>q}$BPD? zi+3O@F)jV?S`@Zi(w*Jlkaib=hN%$8Bi>>*NOzHjdy6XgUywIymYVqX5}+8aU?=1fVJT9 zrq@{Iq)uI%U_`F;a;ZJQu@0coz4SK=Lalc#9z9pK@hZTvL{9q~+4W8UZCbdBVsYk0 zx#qTKrgvsR+2d~Bw|rfH;d6n+;Q)=o0v_z}oPE;%!ncZpx&dJO;wSr(b5w+<{Ix+4 zcoFy8WfY$amjf9iXlnuI3Q$f!t?UoBf!irW_DQq(15rX!^GS8Bdho%P04Z2OsuQTN zT-|RqH6ce~q}|qBXCLi}lZ~W>R*p(rirU)7EUegdS6VF861gIB-$Vk0uLrG~H8?!p z!C~Q#TvC{VFZ)!+7c|9;`)`A|b%ldSA^n!+_lfWKgPXBbS&LWS+>5(!Ua|a--mP|D zJp9<&{kO#QQT)W~LrQVOc)`P;H@XVxNhl#>;JF_f_Lg#tvpgVVA$Dg5 z1VGM?xhL|Tztth-kU>9PM%z^62eP``2|mA6Ek}DSbdN<=hDTCeDvbXfNiEnVCr!Oa zX*Hpub!`%0cc; zZpEYAvn^jsp_1OB-stAmoch>k;B}n>)+%qwL20`apb)(otVvE~U^i;cNCa)&Og{?` zcz^NZr8ds_D0;>Qq|=J{D-ss9HTq#l>0YI#;QX}z$C74tT%gR|9e3kX0zRR^hwq|9 zKwCzRMM#BYJT2naZ@16S%&klZPR1R!4qkPidwA2pVfvG>_!uy<&N3+CC zg}%Wa%|Z*arZ6nsJp~?CussZ&2&+iIR!RX1QZ*7BWAr(UL`AZ9={V=Qa*}RD_iF8w zk08ts0fCsYPmm@_O$)?K`CV4^tBB+oW?|?RW+gCO-Es9}fRKF5&EgB-w~^^af6pNE za4C+7Mt&xJq4y)gE#qs)e5$-U8JltGM>>}8yr6UU)<0O8HFW-T%G9N6pU_F!Bn}J9 zx*9L9AE&C6IEEO;lK>(~WlpQ25v3uSsq5vDreksXYud^ZC7-R8P^(tkHBIdSBDviC z{djb}{_?mxnsVJ~<@0$x+wyJZ`@HY8-SK|@>|BT1V$FmN&I^TWhQRVX?U(Ft*AQ7C zS*P}wJG^5p-wQu6*e^~N+`fhQL)@!LyAZOPYIDp7O+vYR305dmY#tgicdkDJqGF^1 z=kdo=PHbHpeTxjUJ+Gwaka6iD5+QM?+f;(uO@&w<_;(AK(oVG}49b(co=4D44-9(8 za=pmF;9Y})Zu|Zy;^XTtA8-#(%@ZR-F*Y|Wn}^G<;$!}d_MwUvUTD-#s9Q7tXf;-A zY8|HV8i6I8`qK5m-&JEVdEl5uxR!ku_>NAE~_wRapI_Bpu&I) zR+~%is+&MJ{Q#NQR38Iyj8${sOY7`1vmzj8DSLNwb&V(e3+iCkxky?bQF|NJVE#1xn^ap)^cN*ck@a^8vr#>^{pJf;TWIlRO*fx|-5S zhKu0S2b&qRE9fleItVupxMbrV34)V0m}W4lE&*%)U^-OVU<*e5EhL@s=pegL*T`u$ z0tf7Tl6~CA#KHVVc!P~Ar`@;4h0jd9+vWYD{A-odducJNXS^7?%hCzAM@~*TXpzNs zW0u8}&*PYQF731{i309NuQ7+HnA}DK{jdl{t=fVXC|*3}o4VTd~2jzwpCqyv(k9pmEg|0779a?PtRsOhc~oTHpJb z@pD-oGiaZzl8~O~muY~Q%xI6^!{6|mV1-1K?HGBN>CY_l{$dhn&H^C8w*@%e2gpc2 z8G7y;y%8dHbZRv|kP1ssob+;r&@+Zb8M%*bmpVM`52zG4aJ@+u{~sHQ<+0+L5F|K| z^3R8oum1C)2pc~>6xZ@P=dxdyCi=^WeAVLpWCFh`)1?QCzVWDt1K{3a0oQq-ik0Mc z6|%;_wzoqMa^hYom~69dtfsFRmb*>GsVO=Jbn|$6sx`E>m!f;KtFcP?}$EH>__pp0-fB>>?F{sATs%cFl-(T zH%zIXe?Y_CHNqHe!lA82Tv-gRJRV{>?HrRj%E9K=-zFTe6_fS#2_FmHBSX72@~Ii( zSmWJiFbGVUyavpd`0ST>nT~_UT!t4JJj-WhO*;xJo#0@D&8Xgr^bn z8%R)~t#~^(`PGv)?-FSdc~L$&UT8ghXT{`EN~d$&Pw0f}jY@val24X$>{02@p$Ro) z{;P)~bc0*ZPx{A%iLN;nTU80KD_wg?A|$u)QzIX6wwP1?R}{xPYGpSp(g#R=2#HMa ziU~n|5Mp}r;6U+V#7nIx4__B`igbAGkp2O^ zU$v+`#AhyXP@?X?bf2nkBdx7z5igiC317CwrZ^&Q~ei~jsg+Qo~*OOWYZbpm`_jb{wE$J6RHUorS=!^T{ zNTjj`@q|(xy27DwaQ~q^?>y3K4CNN?h7DiKFX<;D9iWu{?ng11UX)Qr0NaW0vTm zljBTkfPxuK>xe}$6f01(-i)_KpzCmVKFv9vk!tq*T==WwmxaF1F3Dlp5%X{ZYgpV- zhf)mS?AD(fP0@9>iLEO1=~7P`vI>Mg2g(UZ0Ec7~gV<~@M;{-Hzt?gUeHb&)_5sC_ zMPp)5B~S>zfg8O1)|Zt46k=cT(U`h%9OXa953%pyCyT5LK|6bPFt>n%e8Ax!Z~#22 zK?cHkpl{s$XqvEYxX~{@7C<=R0y($wiwB2yj{vFWFVQ@G{f;)I{1DhDaeISt++Jb3 zy3+YuJ2Tk;sNuwG<{^}NfGKwTldk#4UGWdUuK>DS0V&{s$>BiA-~dSB07>A0hC_FqvSiBkM-;1F$i$XzH-A3c6O7~B6TV`v^Cp$cB_ClmviCK#06sA!db zNNL&vK+w=lDPkSsS)<8V^Jw1nluyhjCx1g(tNI@18|z!)dyx@xQH31l@`?c#pP_UG zExbg(^T(rdxTe69S=YhUe}^%DQxpU=V?deowAA=1vcxUdSk|&s)v52CU|jd+=lnO6 zYnNCu%=q2U`=3s-8`MyXP{fijc9OEszLBY?4;A*xjT>Hm;3+HJmmjZ+3-tqmKy<&< zE({RB%)d6qk(f4LN7&Bk!Oy2DLVW8C(T1i1p4(!S-)-d8 zLbaT}nN7TUZ(z~vBvR%ekM85iX~m z*%h1;xEjM?)6{u~>eQ-5J%b3?7v)PpO~K|ri=^mq-zg=v74Kq% zZ$`QJN?i)P1Q^)XE7KEe74jw(d$VhWxMC^8ISmDTI+cf-{V0@&WWy$}22| zd5Vf6_eps;p^e3(M4#)>%E9~}?3_#uKV{4szDrndt=o7EESx-(Td;^iI>h(zp^>Pz zwSu2=qGGXC3Y3nYC;iOoo|>vjt>LgV&wqC@tT;i#v&uhRjQo3d=R=1DH64O_d~GG# zUVS;^Q&DN5`6K0#3t1-^mBU&O+E^8QE2#Fcszj4JWcxq*JAhvH-}D!DOm`)-pB8?& zpCTnD$C+YxQj%x9-i10$4^-8VufwxWJ%>v!3e+=!Z5WPi2aq0~%sN~A@e)sp@&P55rmzHI zC3^L%6`?Jz)UqXM0!3&pei&k9mi+}wfW2%k;#jdTg;5ahhAMr~P8Paw(VDmZ2&Mr@ z+k=4i2uz>LJ0ZRSSY;|0dKdZJ5n7bGA%Tf?hI(mx3Ft(eF6@70zy%#p z+eJSNm|igBKMc4fL&7iY9|Pv6NFxXGE9*=D|0gmqc}W|QK^T^59z|JX>13*B-QH00 zAWW9+0lb(Pl?)~a2V0U}6OWVoKp7@`d7#lI1wXrCm8|wgIOCHb@8>DjH(QV zb69+&BCYrCLYYu(T5dva2`M+vBsMG;T{_k|V#{J# z7Be%m#mvkM7Bi#O7Be$5GqYOEU@?=iYhmo;NY^Dl+OT?{%Z4jFO?6-ie|04nZjfFWqWb$u6rhD%{{g_wh|Mp{A6{tr{Utl^%=*1WY zXb+kG)>ghXOY4c;HWO!u+6}yLsc3q*tL=3l*WWuu2s_HN9sbm37`TPfSM%{1OHMrD zasB8E4y1g#pPsAyhz2M#|LnqSosQfm27}vpG3u9k=j_51lpnt=x*ftGSHrAmc0hZ$T#eA3=ueA3^52s$li`$Cq{VN?HPn%uS^~vR@QQZCv@FksrLH($e}cnl3y%ZJ!}A{mALl2|Kg8@cXBjddy%hYvm-0cl zaz3EN>+9gDPX1<#onz=?mO>FD%Wy)MB384<@+4TC~ZTNdgji3Bibu3RA)ebCR1Y+#3q|EcxX9KC++@5cH)-G6MQ1W%v7y~^Y>Rtf-YqUB2%O@f4PRf<~PQ&Zy z14I`V^k$sL|NkiX+rtrX4*b>pf1+R<+5Za)w%z$e!DxjJLwMbl>Bs>R>VB4=DQFqh zXrK7}B&4eMk`Vu0jySFp7vuf8_x-5Z|2GaMFS#-Lm10ExEnMXAq!m+>5e#j=!WNuH z9?k1{eKmgZa%tdb%cx&YeiDckpA?F3ZXp7K_eRr|=N*FhJ6*%;ucRV0_1}&R{C_$! z1^?~H1a*HpGARuIc4S)9dBB(^$7?`jp+dyU1mnAWFwk^gj<-B zP*Va|&5sgu(`KlCeIk?0C>Hdw99AQ>-f>-z7%)*k^l^1$iU98b;4=a6BRl%xfgg1K zgftb+|Mp;eT`IPW+d`1(*w&=l^2q35m~+r&nnx)m20hc-SaNom#zpdvBh!NM>Bt!B z|A!;P^XbSK8T~(w45EZL6Cul_S}pSuqZCrs!1=-3(ze;z70XKKgOBS+5PdfUa|c51 zGhWpoqPlT#d2QeP(_=Up5UvUuXAhjGJCgrzDF56VT0mout2@rmj!3+X!6|%r!n;LRX>FpJfG)* z|MF!_>bxeDJVs_qwSSLGlXf-nXMnLCt;=Sc)*U^q6(GGX0za;iTnFM4B3Ja851=PT z{z_o15lAQlQAw;ro}!c2tgCa=d8B^KKkCsc+f&9bx4u!QxWeOtHP%?JV3cal0OgTqMk4dhfQ$FF!~ zoi9;!AI7aetcm72o^kkVle)TZJ2;^%xlJ$o8&w@p z5V8qjfvczkDyDe#8M1JxGpidFUi|o8EF)niEBEgW4PQaOM6+l|I|>?5C-AfmLdWC5 zb&Ez-)Vu;nmmlBxmp9exko~>lMl>>flVlDqbU}+P}Rt{B~zFD@F*|kY`rl- zTHKkdRWfCR9D+Y~LcNwGbp+y(4&Mwkr{bV##q2PM=*e8?Q};Q{&r=#o{9wE;Jt=Q< zyPOYNsVUOHhQOvc%hwQ=-s9>O`}^Bqkhkwet)kq9QH5b58f$*0R)k>S1(D{iQe)G> zx6Nv4<0WCJHF5u$a)EAa7ka*(+XrY~^!OGqu5u|uok**1qSyuf8pv;ESnWfz@LFNm zymR{9a1)6z^dj*5Tm*wENBZ<>V4j%C}bb7OXm}Y6h*aFw9$OHHg0F z0LPdRe?lchwXO`dxM>LrgeWfQv6}dD?*0PPekE{M}7n zULI_h`@?~eaP`Zn^q)=!cj2YnAy0z$w9HDA+H?nH2Nrfe$ecBgkQ`9QWam{Lc|Pc+ z7s=>6w13{e&&+)83z)M24_MsULhf<`!1VegIt1M`CWZD)1x-ktqebl*^_0^$eP`!x zk*R}VtDf04C(b*u165pbInWT-j}iciWeNscz(9IlXi72Gr zs!VPisdG2vKO_R)%R4iHRH?kxPE#tAAG>GmZZ_E;;9nyLC7d4=D=5?@sk4${kGnG! zD|fAtcckKxs~&BR^ICLAJ5t=s=3z_Jvi<#B?VHJS2pLiMotI&eBSO#j6knDNA_kQub7#< zP^kUM(uO#eko8c7EGUcb@ws*@g2db{d~Ik@9MD;o@u!)shgAnzKUegEGz12dccc!C zz=1-^sHvLu!_4wk@hamY-s1vfA6Ujfqd}kHZzG>~wV=+NOPm!a)fz*aj4A+5gugpC zcQe|xr*2jg>x5EWmtqz;0ue9k%y!mHD~Cp0z53yP9+?|ArrKkxFd>D4NlW!rW&I^FKXEpzf1hIi;6F!m$+Ub&49UoD~|2;rNn1^i@>8I!HF%|2B zFLQ1m{9Z2=C!pJ><%>5!OkH6g4{$HGOMLP(K^H(^WM6l??#`fG`*Q+tUEC@g{um9w z^`6|QLi=oMP`j5S@Ry-mGc=#kM@l-D0Ts$N?@SBp&b&^22lDDn5zN=*;00>zXwjsW z+EkKBU)Qg8y+`c{B%7cG>vGlJHv|N<*;yxiA54bL)IAHHKI$`zOzwoe$LjQ`7WFyi z?ai{*(zN(>k@!b;D*ZF=@5ww65%I^;rs9@lLGt^7bv>R-BW`LM8N;kyXhy0Adlzl z86;kior!rhh5_-5dbq~I-3sQ1x2d0$z}L0GwC`nI+#l9Ls@aL&2Te1>0RK}nP7nnP zRO>t<7x>E)XqTbvn&5N$0+G!$MBGFvmh#kB3g>orm&U-%iUXJ4e zDEa)9gUDg|>}<@U7%^z=+!ts+B!5%1SOGey=}@IA0}>mnmY5Zo7))r zXLkT3CH$Q?VElrel~Ff^4~&RlZ)qc;XTE0be)-zhLa5^U2DkG|R&_b}m5v=s-BO{w zbp-dao{0pXdol(WD>q{6+PP;yQAFA0BnCfq=OfVU^UAYk`h4sEJg@omF+K97EG~o{ z8O;TdyxK4exQ#a3SJkdz~-S4Ulj`|LI$tX_IE3crH;445^(o4QisF|o=a>++j2)#}=(`j1@tJDAbL_j43R7~@KF#XO2!7+eN^}oMz zOk{Fh`M~z3Yscy1muS=6tZZ*jMRy*^Ta(PKWR^Tfq5Z}v!xvhHDE%4Y{`0FlA&Q;Q zFi6*T8@tv0)f=PqMz!n75+T|;_+GV~`>TxQsf4~Pr+(vwbMBJ>ewHh-p$S?xdJCL; zK6T`FwA;|51=POB{l(k->+ECol-~RO#3@GgMyBetM;dHYMFwDRT0C}O)`~ZWdnDbc z+j^@J;$@+oYT8*ge3hC{7N^H04|=)KRuFMit4W}kaOJDWGeKo1<{+p-#jq* zk~(aE!>BJK=1VUIG{QJs@aefd!W_zNKeotLD|7T*Ady`do}b+|yF8n1W{?CZEw)T2 z;c}^Fhd@Icz=viVtI+%_pL2QN;1Q6;8Z6m!(l4G}-l0`j?aMW9>*m+->htNpq_nj~ zUz2jq%-6ghZ~G!41^z&iY1V$f7B!0P?(GGy`8?>IsoaC6$eCRb<>&A-zw$tckB`uO zp%q1u7n*qcyG?;92dA;0evm5*NqtY-5}!^v>6`8A!!+^&PkON!7AOezQI%x4>T6)> z4*zk>-{VF>0C=j3x&=i)wmj_H;(qFf&wxL}3HTU6#*iJtGb$NT0fbAZSjr4Ti#nso z`vUBAe;(huCO=;*2CtVOP$h^^gqgz8j1I04>e#wszmUnyuXWVW-uTH8PrKa>G2DBw zUd+~5`p_QS5c;H4f;eG;B`3rCEtfA~7dEUL_}dbH0HH6UJ%R!`MiWg3gAryGf-!WK ziYxk0HWMV^ovhedYFn4<)YfAViCvr|!ACtrh1ZAoFsx)B!@$am(LB{=6#(=8b>m^y zB2EF#{X9yw(O3vZ?zPZ(2J10goFJ|wbdGLSJY#S`QI_vVe~?9PjlEKvi2qn&WDi;4 zcYXDd+3uqW>n?nMxx?#;?L(0vOQOnvSA=DOWK%CECRFMSPAMDJ)aWZW zw)R=$E)q`twXHk$yvIw%o0j7>)s91Y7rS*Jh7-u8CeS5flgn%LdC$s+6WNp}NWblP zz~58U$G@G60OU#NRIg@;_&79#&su&gA-|2eW4erBoQ%bC>WeExrygs|7d_u?c^tAB zC;0BL4#IE%x4s2=H7bvR+M8cJJ3-YunwKDbOOXCB%?%D3gc_Q|xPYd3@wjN(laY2j z94MRr2E%LR33O5A^RcQo1kjtVKLAM|f}|q@9}t*dhL9c3(zyucp7|rA`YM-`cE4@( zf*vieflV;lR_~jZ_{`JU^*sj{^9G=;Yg?FJZJ8Vy3YtT%$)~ZzH810``MGA>%CDKq ztf8~@gbXGAwCr~}X8|iYY{&t&3UNT5{mQw5nm-=cq+53prpvy9oaj+^-p`NhS-#m! z5l8Re&lNMGx{uz@LS86m7qJ2g`2P@!W)^C8`=o(zf0?LDd{>MRc80tC_IC68UiIUC z-ZrTMU|Q{+FJ8=yXVvx2#(%zG>3Zqo4*=TlB*%Zj(cZFs&pL5ufGdTTlx5yqZ9vaC z*ddq|85O1?g}4c&`H_0q`Kyo4YgxMb@^UOt0v+I$-GM~u2PuPNCh5TyR+GT2+@Ws0 zzCu(Y2Z&S6-7by~Es$ZYP_`M9X{?-ALST?%Ka=7RM1;RRC6!S1J4=DBtC#Xa?nlpu z@iia|J_2L~dTvHao0}y{Q%>~`v}KtYT1|;JR$U@AO`IBdx#$wmtv$we-Xfv@D(LF@ z%>q-)a5pyzAf|Ch=_brHCEi;lW+$|H0WNyAzr0T^aD&Dy@ohHMAsi+M9@B@!%nNT9>$?HPn}Z?gUChvB9``xVH;=F8_##G*VI1QyY|@WarbtmW7?~`(4?GEGh9t>oSebsi z3A&q1ERqh~SA}+1ys-ttwJfF@H5Fof2Nt&`C=ITTq*Esg8f`1EpiQ3i1z6Bg{Uq7NKkYyNL21 zf0qKkV${+t>FRf2>lH{elYiwIwBdR*Y{uxu-3~PI>CC-& z(@?A?7$skHArK)l!r7odLaVyhIT456++X^NY7#J*owwXGO zcCcw|^l)`%x0>DJp&%Y&@ew`P$VQEivv=PS^Y=y~`qgQECaL6^Ajk7bhvKm?nS*R_ zcpk+bq&m#SoADlbtnpn~8MqF~^$OWjDd5*LKSnpvU$>zbB!0*wrD*BEeG3&F$nW%t zxnR6n$%E3&E`;iN${`7CV}$6Eox{CK04`{0nMo zK{q?RVLPXo2WUK5dRT%9nOwpihO|U}+|!u!jX~I^sGaT{YN+}Za8r8o%;aHW4IcQG z?S}W1p@sO@OWcnM&LQ>3%B^;;sB;%sCiYZeTTpHXi!`H~lv;~iX^j5H*n=TEM_nhC zoD$>&p1cFt0psN+4H$(fskq0Dqex>`Tb5 zIAEnvNuM)pJDF%XRbJxCcV*VNCT;0(#6N3ud>>?s&6S z37K6{Nz1I5OzS?i?n(%sAI z%181gebqEL9OF3smni4v^PkX{OPUZw3Yal6|JC1x7CI_Ff|pZu*KjtVN(pMD4YEr& zc}`pFee|tc-H-l~w%fQ`#;x0Mthw|r=Jet5q+9552rS{wH2ee$`D%E{zCfIs)XC0_ zo5grRggm@AxSr^QzHa4ibtI3Lbwwn5#tJhl&vR1J;IqOxEP74cfrD>z={{PY;3ELBn7S@tn&D zt7HOZP27cxRKy|Z0)Na>UUBBD5$QbrDuR@}iTg+~&F2Y8!tX!H1y?VH^g+!ncTQOB zDd*%OpmtA`vroQKy%*Gisi~VbAtS0V*Nt)jcWMn1t*HKXYT!rvvjx~(3n|M;J`p!> zS4OlX)ETyAmHo*Xud&>LCi-vpnh>sTEFXPOZAb+!&09R>gN!KEuZ_@j#{Y zW&p*lKZ`Io^Riixqx#AR%SYF|Y16hHeCp(XqJ8R=LEk!sAiMh7p(Dk*&ic|#k^MUs zQ|#4z=@O&Wl$Ikx*OQRZ*-O+7n(wNx+6l>77!K(#K{1Z|3dhGlGGqlAQpIYf*W-0k z41+0FhS#r(W*GKZ3=_tI-KRE2cglp4#6WM{p!p~+BtNo}@O+Y4IjiC+(&0C1?-c#8 z1eI7$kj{lY1C7ws2-PQ^f(q3LJs)8^?KBnrt>^Tir=_je1CAbPCdkB&K>KzH2S5pq*Mex@OltJrm7)x>f%gvVkO|{#U9bnTSv*lbTAoQtvGmp;)55o0xTo3tz7;wRwB0)$e z&znsP|4ijc`cXXsRz}5DEo+d|LpYh_y!jOX7yo6n}VJExY+*q88RrYuBv~r7NL8KlPXdrA z$tc`K*C+U|ODeVfd%l$DmmvCimu%>d4cM%lh?UdhfSoMp>g9VRKOUX8cYLU3JX|l$ zZoV?kZN3+-%XiB&HeiP|Rv2ETL&JXouEAT8%NjAT&Ks?|dDO1{cKjaNgZKzNiP(X| zc`IXr%6d~8ej*r5D9X+x;b_s=cn>3X>^qJ1kc~mW&$;f#n?)M6xmDY8`fQgqmXmb5 z`S8zDv#z@x83wY(igE88w&U2n%wm|vC^pFpNrd8(TW_!S(A z7-19jI;Tymch4nCVcP3NU303N$c9aXYj5G}TsGc!*@2{T3sny^k3yx>&#$~+XVQ+Ajr)H1Ug@@ zJa_rnOynuw1%>Z}Y1=_W8{?!fIB6axm`uD^-6U()C)1UeicfSTUpeoCZZRR6u0NNN zx|ty0itZsmXQUqT1E}NF&6W>9i}0~Yd$QqS`yePL!PZgOLr;ENAs`3sUOY^eYkhz! zGwFXye3|-{vLr7S{)6mEEvdG=;7W{!V->7+*B>e+uYP4&|XLnkptK?9MEb0Rs9vU1%5<5#aO`*l_d9sCOc|8T|wKlGFzg!J&eQBAae-Q zhq~P_{xn~eV}yI9j>*1N_p8*|UvAo)90 zHs4W6Ay1rI+jRKlX0?7lXO?!Itv(X75gGm&yD2+qsg>2Xi|txp+ar-Fz6@ui{4*2^ zE@ge>hYi#0SC#QseHL6N-vZm$Tq?f+<1F0FLW;TTz7jAx6Wm8woBKnNad=n?g$WX2 zo0P^h@#rRED#HjDjTJtR2OtnJfMC?1n95jAx()fYR7YF`!KP>Srzj2q%oANks0A(2 zbP!a&3l5^+a_tN>+RLVYA7xpy0^v*4uIQqg?-!Zt1|5DX3pqU@VIjlb;Dn8Y-*Hnv z1e3O<0-bfjh%%ATBO<*&&cqKaalWNzE3Lo%KrjB}gg+?7D?vIVN9U@a*Ug zuDQ%<5B)AB*|a9un9kGApTONa6H{=D<3@HFf%}&5EQxK?{2Sl9ADDJNR#eRx$&6ME z#U6-q)~0c&R};Nxu&yT_fxqk~L1v?(TYg)M>`-!K?sRI+H=Z@?o+3|)s+m4b>BfbQ z`yrai5=3pC16;Dk?Q3-;O@M_&Ynj@&*M5Z|*GWVJpW4@FqYd4YCe_3;Pjv29d27Yh z!#{<}(jVfIH*5!dIYO!o0yEMq9u@l=>c~^ZpUP&cK@H_`@<(YayxN!i1w1eS#t;9l zAFt<>lWa&Ry``dmbBLkzZs@C^KXp&TaO+TpJ<3-BOQ~Cf6j8F50PDtUTg+HvH8mVe z7Rc4Fl8i}g3g|IoxD2jA&6z&x%o?{_wt{4GU=iU=z|42hohKpW&{qX!yOP%6(vse|>rUBHZ{Ux@nPtE&w<_-N3qjp~*#Ok!a%BjspX1_Y8Wl=#kkp0D9)uMEZG2T(ZBd6nAL7zh{S^`gq_ybBD!#k!WDz_H@MH-TZ8%kW&dlw+g0H+q+pG znBFD>nR0SSsUuz7ecMBHu6{tD#BG<2R?isKg<(4q+Go5HmXKU7&VJoS!!V!8E*Gr? zJPaJcrz!QtrKSwUWDuaIf4@0y%&P1P%W)Mg;HpmPqWQ9#1ppt^w`#U& zFzwv9wzjrS6Y$TZ$1bUemhz{oJH5EtNJFc7L6;hr%hHKfW?X6AM)1XRK`6lUg@qpM~}a^?mfh@ElL*m^F|*GK3N}G7jc+ooCF^P$Zwnt zPAAt$=R|vdhqe0~M#^aA8k%&5WzujjTa%W4f?WJSP^-E11^b=^j1nd;?6VcI3e{I-h04=iGI6o_W)V2%Te zF!dAw!%hLiJPyg&{anIni`Wh=U)1CLe3utlpAlHi6UL!B96iTj2MWSVdt6gJJ>BSh zr+2~S5?YY?T6|edcszp#UQv{4JOsLTjS)4vM0Y z7?7gn_;j)|aJ1R)V`ci0i=f7}e?ENd-P9m1mfv((L>!s~<@p~njH0Zc@dJp^XG}fB znBYVC$gF9M2zUUBK5KV&yB#Ues?0#GJ1XTpD&CU>Y=<(g6%`j_JdExsg??}$ULZ#2 zn@*<*Q&HAt^4#BDy62n1JHc})Er!hfquKFj5)ilCmaoKdk6UopQ`ur%b(uD}dMYVD zUIr6dqD4}oVNpVKTi7aogcPiZiG@J4n2B715vub18Rbnfdg)w>nkd7ZD*kU&REu&M zOH$d`Egwt?lZa(g9T%}d1LFe)V}$LV2*W`c!NIof5}23zuV%V?%uV6=FrwW{(L|q2 zEpfg-cH>tX#SDomz&33t?!gy}hzp`ll#o>>ZK(GL_TfjGF3*>C#w=1b%BA=sxv3POX7Z(FPZ9cAn>}u^|wCRyHm^=<$l6m>?=RcxdR50s{j{etEZk zd-{3gdL(#kR#=mAqYSP{Hm{mx?VMYuyv7FH)4}m$?09;F8OprNo1_*tF?%jxjTLE^ zF75f(z~oywXQYND(1wbqDs^!=C9zjJpp$5M|?7#9otkktJ5(~VQ2VvIe|EQmjL1Wq|7yIQ}@BCIZS-IA7 z=Ypg~9sihfndiF-hrA8c$%;JmFHC923L=sD%UbtWUtH*rmVIL4aB6|s9JSxVEnPB7 z2F*C^uR#%#vuJ*b1N5m7+)jfgIP(kyI zBCtUz{zo05A-Lo-*0KWyU%l40m2aOW7R>;M!{Eni(A8d)u5uXB-lmLQyF;@rfA#$`L>bXDOu~o)kz&vgrX-Xo- z#ZSj~na+sKIN-E5HKufp$p|on0$Db^?j);V?Q7msxUobyVG&|7+M}_6?`O@MyqwttkeF4S&3m7V{bk# zDhA?aJv{Nl6@zRiKsD{UW1!~d`hUSnpV+s}By+)Zqk;L|=9b`n4Z+?J7o8B8D7acV z@M~L$kjiJqFc}lR3>{`sw#6aW32cuN7=dU0hEP}aW8_?$R><-AL(}#CS`w~>{ev!EgqN5>R1$XQg zHc8Wf14p9$dUb~KtG@IwnsMpmsW%RbX0s3%;an5DViHyf^()=?!1uP1xTw4ZA}XRl zqb8xQIaDR{0sI0tWt}n6X&$S0o6&C+oF&k*; z!ix9f^5Np*pXg+_3&e0M0P3a^UU+>LzC1+Ag6^-7r@Zx10MR)eL_=)>{L{sMf-n}z zZwbJ(pFoqgbRVwf_k$u<(o@~t+H-95W}hB|gg@{>$!}^7lDQO? zwc=K}3^MKxVWAQ%6Tkm<5W6+LN}ZRfMvE-|J?pJpU+kkOcZg{p-*AlcXwp2WbWf9X zg@G>jwN{J=gK_e$Xh0r@KLC^JCVVnm6exB?Q%2Me5SX*wm||r;sik|CVU$IkNf~JB zj>>GCJZJN#%nQ;Jbd2p$NShgbl?9vptoi?u(IwTCsCt8Nf|9Whl*8JbE0+D_&V~P^ z5Z)~_K{jMld554@)VdaFz$YY+cCUk{)ZmBZG z)^AS4GzAuF(pYI@Hpkt2BKHvfwY6`@? zgx8_<&o|xho*lur8A8_z(vvA_d7|p>{*K}86Em<>u8HI`YJe}F6VkJ&_zvhd-OyQl zW{s`j`RsNnqyhC?&L?3pju`vWI@7LZWOL1UZx5bG^yzzGPdOaxt?b$T`y zW`mmC79R9zJk$n3mq7a$_g%?j(QlEuNMn@EOyB!`4{cnj=&FhBl*iKMLX;vbhV=Xn z(98O#G0{z_Qxr^Of#R`oBO^+F2X^4FQsRuoU0jq526qI6at>hrM`S6vR5+26JfpD9 z49J|^{DWIiLC9-QoFf^*RdA+H>E_Wa`{YuW$svK)QF?6(bP0&(fNP>h>?%Ludv z3yCJXP%6?XRzie~&w$fLsO~C7_n2fn7e^CGwX!Z%6j(?8(0{VOeL8n3a{`p z^Nacks&!%lj>&VIHkPR}!ZUFP1XoLd7`IWM@TmUMoIRJtoXbdE*l)f8bNT-=^sOA}{D7AV2 zYdIV~rdG%SP*Nf>k5z1lJGFV1z$xcou+xSY{4T*|Wv{b_f$$nl7`|5v+;1gt8tSrow12sJ)T;1 zXW-8c>LmXWW+`@=dTF)Hn?lHojMIhg_tmJBlra%IPU>;a>{Vf{C}LDqxkriz&Zz<} z6I$ky7Bpvk zuW+i9_Ci(KEi|piZK>GH53>%LpUbPmL|Xu-lAbVEGev*a`6(>IHR>!V4QZd0ixb81 z1yWI^+Uo1s8n>NRkvIZ|>-v3OipiBRT)*trHgWW9Y_aiXPHqfp*(i(EYR)v&!upE) z4;x!ii$2H>7sVCMBITKR^rn@Jc$W#OFGQ!(V0|S)Nn^L9hJQ&7nBds)EcD!HC zF}6PsHZi4bwsRV<^k6Ay>ei&>I?>_8zRjbLDx@Pj$ylMmt7<>6gc_ufp77+pB)@TY z%;Zi(k!AztS(nfpzhO)!Q=bAYQzd>rbnV}?>j`$)biA-P?|MG!PRVI4Fdy=`K_W+btb#GnE2QXKm2%07k+!Jq+17hoz6dSf4oZJw=1k=eK@i8 z2;oycA99?Gw8)J-lPWv9GO4AHDai6(VX^$7el1$Mu&B-~ge8#KnbQnNfy~AoOLL}+ zT(=SHr{rWaG%iudUr>+dals2Zr14DK2M#!a-X_RCUN5S$KrVJQQJ|){Awp24ScNvH z?i2@gH`_(i!WvUh&e1|&8LY$<_OCVMx;$m-RJ};zEML+_cxG%_(uq(_tCW!Ban*&|^IlY;(y5Jw^RQjGT}>ke2Ws2%E5*@Tm4|UICUjaA zFAggpjN$hij^w9mFVD}L?kTUl{{V&+8@^ZDUS@Ofk7+^H)ahI4#d48?eW#`H-O?+- zVp@jh{}mK_&$sh3_XO}DnjkKE^Q}VXd2hT1Rd#k}F1|e5dDS!@{IiA;^`lG4w)X)7 z6jI575{5q_{nLpJEkjT^;N|rl!ur*DI+3UGSityAeOl* zBOI>$a@cmTscqJTuB){9!d1{s(l+si+z&g>dqo`TFmczrCekM^kC;6+C~{+4vqUpK z`;*J4Xab%%kTGxZ3o$jdV90TM@!JOBI8m;Q!2Db9N5LL7@1aDs?vCJc-s^&KZ(2UE zB;b=6vWkYfRE|~gJjsr(V-|H6O0$~TD7*P(Ot1L{G;LdDaL>87IG{7$8c}sqxd_xY z3_xI64UZaaseY0zYT9G|>3MI|oHmo+jm!p>>FjU~FyWGxG5DQKGUU8G7tttf*AIe8 z!u)a$D5R3_?EskF=@h8>Yxr!Ely2}B9*VzwISi#t2oVN%H{bN~abND}_L-Taqzjdu z;HRGtQQg;2BqE<&(=cykqK5Ej8UzK*_qrKQV4HrG|4!yLn`Of#(xku|En;oLmx^Cj z%0r#Bi0uq82r9DqJE8LSEf*ZaBStq5Cq)ZQbB0Kd~x zKevGs<)wvEjlZ4no(Ms_IaJY&^w*g*<b54=-II3a@wWChYy zQ$z@#8SBsd{<$7A`P(hm+oAMX4sE#l!>uyiXr^X?gjTZt)Or{w4bFuOHe6x44O|VM zaV|!;wN}rqNK1{=QT1htTxrTC4{+NQlHk}MDMN-}(&5_pF~SHqML{^zIxiTJl0^`9 zN?`0`36q7Jz00QgQzGIFD_V2Kefe&EW6^fMmj$7NQU&*j@maV4c;1P1~7VW0?wf0FAoZM zx|Eu5K3{0JhyD;jVwD6EK~&}rt4x-N&Vx6W=&}Qbv;^q4a1+K9rg^d}Hr7qN#vZPl za!i-7eLbU6Pnm&b&h5J0tP;m!Gv2TIBw!@b`a+z4i;RRBXs=@&pAONCes6za%)JhL z^lj#FC-7PsVoaZ5_pD!=ng#9K&n*z~$-;{K;`8FYcDXYs(lx~hHpmQCZ)75S=f zw#Ys!dM(8}(>32-L60HLFtW+xU(!dXwA&cFj8x@cpM20v=HFg3hduAM}5ES%EGSc{T?>E*mNj)diQ}iUu(EuR(Q~k{ovC8l^KL z`?zFI+|-qi_w#3`zc99%`esJfk}R^Yf52#tKVn^#AHH`P@)*Ae+y{0`F>za}Dq^@a z89n`3${eH{uO?+A(Kt#PhfYTFS_qerSwYhRz@i59}xYW8jo`JGo%fAJm#m5R&sVz|8D7s5$Ns=MXg2ef*1=ZWza& zwD-Zc-2pcW4AGVddO(AjFLx~$1`)1KFN_n<7u?qZ9VJUU!We#ZzwSDsw(v{A6(TNb zM|UbX^xA+Vc&|YikxjrAUw11Jm=|7us0V-=Mc3ZyMa{KP1D=V)9TklxeECe<25bq4 zx&h)K0aZJ0F`PUyWSM>d>sgj&&-j}j^v-RnpF!04vZb_F4HP&eX? zfgTFoM`Yc|euQBPVXq`94_EGekMEx)GJa)|p~Dn^q;4aacDuH{mo+Jqm*e}PcoqWG zYb(E@Bo>-qa-EiKylDUIJl{wftnl$0DcEaQGF+%&ajN|6Z@uR0vt8h8dJwro6)$~9 zBw--;uu0T;c|1Wr!pA|`38LjQ-cKA~ESrg#4uAdXjzg-CWXW)ZF`f6a+3#>as zy-DV+@Y#udf9zTKlFSY(TxTt{8^%Z5%NLLO!UeC0Pu0noQ06ud8RlKDYD&NvbtW7- zMQ~(Uf)sj$mYSHW<*Q=oaE*2u2pK5IEY`9Fdh7urahfM}nn^^?Q=ycIzGfVIQS+2j zIY8{8_*Hxosjq#TA2g3VG2*S7TL-=d<*P@(%6 zYN4#7UjN$8f^$-}%ZG_LVuUMIlE#d>AnmXN-$)C%_sawH!S4s^;07B@P*Rd;k*2tH z^fJL8VAInfXk^U1ig5OGIIwpFdU(@mXH*;c6j1tQ7D1P8anl@`5g%Kz^f7i(?tC|V z%6qg+S50S>_KQj59x;D>T07ZZwFkI0OG)>lHETSyX;o#=$0s|>rN(oo;XX0Kg*k+tHgjRTGltk2N8H>OIAdgBCsemALn_A|To@B1lH1bi11(GHMWxTME?pEKm}YYLLl$R?oV; zYM+SF!+cFT+q{ZvH*~)UasxdAR=uzFUtfWupr&Vi(6!&2nO^Ty^Fy{>yR(cL(yc3o z=@7qK4@yR+tu6{b1iuCLscfqYJCuAmLJF ztE3jw(0`q}3ldjm?^FwwEX@d6MT`^jbhgM?=;PUkr;6Mw$d&Dl$})1oYvw%1qz{m} z+8md)xI-5#fhK(QNH414cP~^iy9qK1tg<<4WW58ytJ!?Kqc)NkVVjY_w(}yKvH0QZpm!k$f)%eP3B&}IiH`h zIUIJh(3)lc^(ks&TKmgiBXo=RQ?%NWnB9K8hB(zz#VAWs9{)K)P&FJ19+e%Rqpc5Dh8C6) z+r4_>mfJOILgLFoOM+G9<-?Mj>OdsH@1Yf}fB6HW8B|{AB_oZ3by<$%?h->DP~WAz z_g#BvT({mOO2WzkbL)4_eA>6cl+1o2E=k@g32jT%&LH~KcHkb3) zcdcmJYst%>;xC<1;<$>10oX^2e!uBP?OrLdp!cUN7)g|Z`ghCtAMKjGR;BSVbaHqw zd-Qzs%k>c9Qf4AkarqIO9`dw27H&6OG6XybU&|WZoLKg{eg0C=_OgDkN^x;Tl3uIg z?=kgP5QUO<^w-c%e=S}kwYDFh_vl_D{a3DgkBaELN|la`&7;*~G@e9`XwZzS&CgPo zc^WY1^fko%(YY69S_Wip3HiT>ddJ|(x;E@Kw$-t1+v(V5$J()N+qUg=(6Q~LqmFG` z`{eoF@0{~z|6EnOR@Itw-q*Os1Zheh5phCDHgp3OOOfy(st{+*7-jcBeQz)KIT)R} zJFmNYd44I_-d^ePQRbUG@qW9fS>Y=)2$Vmn>!6qN}K*haaM>DF}l!xeXzlUEbtEa1yLH)vXqmC0&I#XxtdrDV#c09_7z!~dQHn9 zgeVK?T5A=|AN>fy#FC5bq?e9C@+?yuk&U0)Z=t;RSgLz|R;r|zL$TCk%B1&q~e)Q#)c;~l^}g-fvcVX9^QoV!O1Lzo6bP<$;UNfAN{ z6qBZ&7ny6Ex1}WKtgkNfAGNXy9GmQlgswBE=0hvv<+gX?h6?HvG)l7?I@L?gB?E($ zb2fy2wLrZknigdpB$0(t)h6lm+(yE6#Kj@Z_C%{(G`3dx*S}^Krl%)IluTN7X`_te zyEcC|by5a$MBs6_7MhGz5G#}3crIOBXn|8sM_s0A&j;uQqGg=RDSOedfzTzV1SXtX$!~2yHe-n21Z^8= zU>>w7w9^=FBs#w}zPxU01Dl0O+gm@F`1rg&wsSS>-Iw3;)%mSI6+6GmO%`I+OQUr2 ztF^zSuBrcco&OcjY_v9Q-k$sCJOd%o`dfzm{kqFX{kL#0>YjnA|7~kMfd<+^?T>ad zZ`Ll6AiZ7>oC7KC7sxIYtf?$~J~!1|L7~rR9&)ZbpI;EF5fH-a8FA+;Z@m&UKC7k(L*<G0FJC$0<`g36_GrDiR1Hw15GTpe40|Pi}eUQ?`2RH4NrKB?Cb&) zKKtDo6E=wEY5B+{%?fPr@-P^4JAohQpo2k4@wL^)2vnw&x19`=bL{iHD@Ud1Eexsa zC${y=e=6OwxY)~7yCb2Z=Qe-B)~Nz&B|`;>-0v(nxPP!cqJ6NB)8o30b&?OBiGi@;p=#Rc7`+4CoCGJP1O? z*f`;nVuA4>NGK*3Sp;LQFQAuXze0I#M6sPq3&kPJD1#lE`rC;uhBM%rGvXmP68^f} z5H*!RD7Q$e@Vlgh1u9tH{GXo};uSFuYGAt|)E!G1)ZGToPmjorLI9ew>;f9eJoc-w zx9*-fPX|k}{}nq%IciCusv&E2Fsb3ow+bR=*R2R*h+9gsI~KC5Ok=10t`>i~H>7xC zutS5NCq1f#PCVN7YC?|&_hw9zI_7}A(#+tM>dqjoSjdY7ZA)Wom>#ivIf?}JT{-p85j{hC3bfSUlCHPL_VB8c8JMI^E|b($r(7@Q36;& zsbz(zWq))(DuoZBrmZ(Kh@eLxE9-r*DSB5iB#4X%=2S!qYv6&A#aGTqXq+|(|J^!W zu+&qH4Z>UmHtao;T7{_Zn-Y}%#_(_kjIf$9N$HpLPztv+28~(s!0mfa*THIt#vfVV zhB28UFyoadmlS!MY5iY>OA42`5{XMxWCv;nxJ?R|8a`UJ3w>d-IKSOI7O|702kiqu zHo{Vj&YF*^e4YdkAxgkaSJV?ug+UB#ZlL;(n%CHARx>Y>{1-z&;gj9={)q?0>c-GH zu11-{LJK5&;{u2g;tch*!s@~M&tm^p3`gnpbaL}vNHB~tOna=`oXf|Zpw*&V)KOYO zqhT=_2F_oEVUS(V7oy^jar|n5G$K}vQQ+7b$lRAJ+M~&DHU@Pcsddc^4i z7RuQJlYje_Z9UgwON)c3GttjCgr^Hy@7G@@SkRTQC6oJT&q_`bwB_k(I3oz z!*!SM827bbIW8(z!cmLi<&m?`X-*zES?^g^1N3O@69!;vJn^02)D%gS4YreF*9F6` zLX0~gXA5p(9TWUj=O%Xpj9>MX$^sDmAxVB}S|e`BS&PM&Z>4`Yzfm$u4sPNf`y zUXzNk$4QRzDeVihyVm+@osn?afN~uaAm@tOx*-2<%wfiZhgh>*lpJ_yB`iD`+JPsUw3=463p+cjqZ~GTwX6zhw}o_ofu)@ zrES$ne~M>z#5E z=)%v@37h2X>eiUXOYO$4(D&v24n;IC#Ms`yddVabr<>8gk9ukoSV*MTsvGFCLc3jg zTM>$58}wd?pP>UkAT&Cp{bz2EwiO!|B}6(OYjBcOlp!Mp`Tugc$=k2YDgHo;7XOFa z!Qt8KFrkl+D}r4p=&euR=!SD!slVSPQJrATzjgI~s+@lg#mnA4)*w1Km{awoJxboR zk?Qvqp=Yl-&X%m&Lz@m4PZRFMNUf0*_)w z(5_2R>l{uv43nZe&%bd5-Yh$B>1+btb_cULC3C}PoK{Sb+E7&v@&QB&DiO8MgP=cU!Z z)OVR8<6Q(xb`}n}rXDeHRt(lc{kJMLmpnS9ypqiyc|%TZxWSXsPwqB(QLDZCohqJ=<#gcRXY-D+Zmz=|Kt zG+=efh9T%%{By&na9BK1UDYsleeAHTmM^HH{Sjh%YfvqDH z|6g=tFC4w^>6XE2GpuS@`;>st-Z z-+FT|H#xqay{OzHV7zFo%(0?5b2%l$FuVAdTR)04k4|Tipw{MtU>O)x4s?69Z_wrY zChB!3{pmc941luSM}oVMg4>Sj=J}hI>I53Kl7GW@2MxTE>hhtzYd(ldS8gMKT(C{z zA${K?V1d>3t-r6pe9P%OD*$``FHG3mBY5W!L50xA zFoB!=ccH}+{7OE;-4mKyJ@9~gNFUaeLSEaFcf(YX^b%;DLVm)iAW3gdumxQt#3T~B zBYP$_tb3W6{5f8yfuC%MJ3P$CZWTAaoS>BQC@T#?OFT?70f^#&z(&qILYadS36fY< zRq}yij4B9NSv&y4A}z)~^+Um0BnF)dl_A&NF9OWgWb+i&esfQuORNaT^`9A}6@$lv zL>Wp+;tHLb)1#fdXQ`Ci`pgV-%;B(bMI^;lqkDbbzqM<;5BgXTim0ET0=Wuct)R9h z@2Vr-*+cSK(;5!Gwsw&8()bF8SBo5I1_>z7OaH)=*DbFc&r;}2EiT7#K35X2rb#>! zQl)1d6~b|5v*vibRxz;Jkvw8>N$S`41kdIRl&&*J0VsA4rMEnprhCAGj3RGb{=!gL zvX>}d{(gGI^$!H3RcbATXQLWn_g9~eNx^Cabhr{Fi?XhfvBMh64uC(&lYpA^oDn-H z@U#1X_mSnSEy&W5rRy9pK+H2+yoTfNCeR*-P^tNr7Fa^n#5GtFAk~DZ=d0sPv|Mek zkdd^Jv*H|TpIioWc?VynpZzmRMA`XTmhJozJFQz<%7Ih1krowN$k9Y$fSgRsv5>S7 z{L!uul=8(|JB%N;h+mA4=iHKFP&sz57^C8OchoRuF(`zR+t=F~UV6}}I@^l=cw^gs zy^4D6K3QcS{dIK9#Hm0z{PJOA@PNQ!OLv2Yy)i%{6HaOlyCj=%Ngj<}!m=Qpg9OYe z^5F$c@h#C+_r@;*vs24cQYle>G=%ZXo;sk#XB?+4w;|p#b&D4N+eS56lMG)_&vMQp zP;QfKMTu}6)!`8xUi-J$T;SKTpu$FpHw+=+s3iRDT+ikkJ+dc}fL(C;Gyxm4`i^yt z`bt@gb4V}ido^VBk8+HZ#)*2X?8363e~(HEH+;U$St1Zx1|_Ajj;A44ieB)V^Xv`P z$#q_bix`;7Ft?aHWI*&KOFCz44h_;%k38mOKy7Z(<@Ju_#dearoV1kX% zBa$R9a&et+jR->xq1KnjI+nv|;~=Ia{D=c(+~wXUX}(=D1=lagFvFOo)v-Z@NhK=- z7>STj@4u}z5ZiE#1xGUGN#)8FK(xou%-nxpzRAm*xwL&(zinVO8NpYj#FK;+qnS+W zr`b-n_Upnq08#sKxvkz~H*_83g6El6Kn`u^^T;z*=*+IUIbv55nh#XC*sS{GjW!%^ zpad|R(nj3s7N-8T0#nAO$QXE~a?19h*2iv~N!Fd-!EP31!l9|;;q8QCh6*2Wez7?X4D2)c;EY1_+CtdUF<4Z#(UiB4>t)Bt+LFtu;ZF->q-3rIl7^r*TJ zt$ATq5MwtIO_52gf=Sj2GYNsG1+|)A6zZ)R?`J@SU8&UZU8Ig6_QG9dbg4)XUNjDw z>gH&=6gQ!!(XGPWmXubmWaZ5G!O&u;zjVIYmnqirU9NhtTLELs{0W+-(nk$rNiB=1 zHFzWg*K@>6Y5JF?DmQo?YiswnFclzUwni=Fcs}#4JHGNvJm;+1ZYQ(Y7hL^s~cu31hxrYbwv6v|YaItQO zk`UDl(WN|cqP@~@fATG*T476nHG&_80*L%YwL*a2fCY#~2T7qJ*i2cgB~upjl4+9g z%~eJ^MV$kIB2Dlz*EwA0zhQRTHwkn^3X9gJGg@4d$#;=K{sdV46@OM-0}c(+`=zu= zZTU4l|8{}pFf#MuuSoq85-C>B(NVN1iP_IY96gN{(J~*Im>oxbSd_iNZ@PhD*Kez3 zf$R|fU@c6V^@Kc%ImGQ88w;MpUqzjD3aC#TH@B37tXRPSZAM6y{=yP7hxYS z#@rlL1K5M^dA@GSr@!u9|Ev1jTi61dqejW~9pcpF1N|Fll=6@WD;rTTt@8reFbJ$G zg24jm!`nj6#T`wRVB`%Ely&%;g{dpj=!w;fwC~~nt}`;{_l@;n?UFP z!>)MZ#-%?=QN(+JYuUbZ+lR_$1z+agd$`4>)vE~TIs7CkSKow>r%VZ*_znV5>a zkxtGNG1D8W^~@WiN}Mou!{43UT>P#gzZ!@#u#A?JMp5jsj(Tko+CwP?I}!D(KP7hJ7P&srnl2e{RewUfro#p|h=_vz^v+NwdAmbhxz6 zb6~_%SZS$c{{91N+?2HlAX4ta0qU9eI*QNOD8kur36ypGI8^-i)%&Q!2eiHH-qL^f zuQ~7p`y!%4>VIyzK6#6*sRne3 z@9(#S4L3_I+%I6*;PTRYQHaDUcLR&C*M@h>gIub|g1H$qgrPDFwc=R*qew4eCEwXE zf$)4^_unLKr)oT~v%516*qC&Dz5z@}&)%uV0@uKGxDMRAsO3ryCBg%?MI%rCSH4uA z{Ql`)2G$*T!c;ys+EKP_4pGZImoocb+@tD(Oa8^xvZsHFg$y8;S0_F5s)n_ zZeLPP-=?TFhOAJbPrGKXwaG@(`y?cQg)F%vkci5|Emc(>EQ{F z6i0+6=tWT#s_`0_f=n+CxzPl?wnKin->%-QZTWJim__FX3V;js6Uz{fklI6cPpbwG z;QP0Oe1F2Q_Xs;BG0bkU<&$B)Qw>{waGWSrE2I*&QGpxq2@#aAl2oujV#p<>R|>?D zK@kY-#pqr~B^!Q|TYwY}iU~@2I}F-F%Xj9UGzDV(Pa%p_N?vn39+@a!F|6DIYzq@Y z6`^=*gw4V0S1MBQLub&HZeU;1zDf*9AgwDSf(j@UhUGraT0GSvJJPrx?l2ERw5bFi zIgGf7w(;@Jv_He9OOoAGdUa5s%rK-HL%Nk%XkJR7o2G0zZLaSyg+QAcpTIw8BBhC| zB_L$~FX1vDz=Z_W3ns!mM9@MKY@cpMk{yN#_d8=a`w4U+s5u5)+#*U-rKE;Vj7Wmv z&=G-ljDG<)BsDsZl{X%U>0&kc$UbrfH7p2=UAcOG*^wn#_A*$hqUK=? zd5~DLq8=%E?83jjKorzqABai}>h9&3{M_QCF+}6;VFu@D-7+l50_D6=-W*%D9NSXT zUX|zQh6~UCrK29{k`{SzuA?!z9<^e*5>ORFL`)!$b?|i(a2MFr4l9tFniVmK)atRx z(p^~U#TcD#x2dQ_2*OZjt73dgP{J)nE{EetM^UnD8hS#q{yGOh@ddvW#ebi(SB8qc zK+jJvhW|1#usa}l`OeE5#j*rws%iscIqty*RO4TW5yoz-ClCW(qOo-S5QCe>J0Sbm z4K}8r023n?bjRlxFf|W&`gbeq`x%WlP=aqug*t)<{8D4T7S-O@|2W}zX}SoC6mxTg zx1yVcr3hCEvDTJ_XDNxLc_NuO+(W%nBsuBma=1-lA~C!ShRT>Do(5?e3-?tm4m5X?)Z@H@#is)A12#NZRt(GR;iKa;ry;O|Jyy~eHRI(wn%eLhb)8iJ!)qVGX;b|P<{AKWnX`k6C~ zy?zN4KQAW(SW4T#Zd5bm01BWy5gu)ftyWUUmpL_a7zCt{6JKh~Qom9t+0F)o?!YAE zB6@w?NmM}StN&Y^`QK*1ez6O)JSSp6kaD9C@H?)^V{WQEDHlG z5Pm0l8Q>L;XuWiaPgJm0)C{UQ`a@0zjrc8zfubaR_94KscVgp+l;8c|D)Jy~N2oG# zj&dR_%A0O%{Qo=-j4!zRZ=);3G=0(B3_4NvxbTrD!0fQEv&+y2Y`F@#dWLAn1Te&y zg;*VbxhE|$E6d^FlSJt))?E6-?Sagsv?2YN8S_fiR(&NFN`gTgg#B}Di8LQFA1+6U z=xNty#+nM~U&cyVUe?jwwGbzpYDa46Sro|V)tSUs0q3B*o{XRRXz046Kh970Plx7K zT`qWw?l;6*U3$8Oj^WCF7(`nt{>V~B)aklhL2F*@M zaPq`CQqG8%T=vMr;quzPPO^N_o0KcykDHZ?RZE`4bSbVu62aJBk7af5`9#bASb0+Z z5#^vV)ZXU*%G!m7HdSjcftTu*wcSO z^n*#ezL0jl%_}a=7HslCrZHL|RkRHlG&%)9BuZ&j$JdD&JHSY42N5r_R9Mk7JK-@z z861Z36(T;ODYX;Cc0NBrY#v+PKLX1k z)4jh{3O=oDBZiL23%W1%lN=j@suL19GwR|Z!%rtD~+Cdo?E%Vk;s6Ot# zQ6k1C&mmXb?}G8@J24CG9l4Bqhf9S?dJeDIby}ebX0K&DtxXh#9z=0d-^;9i1m&kL zr!~XcpBGIrOA~bZj4hoVY$*U3aj3BcCXc_$`xZWF&qE-agARNfhgv1pI9@kH`IN`6 zEPKFE_4kqhiU=BI|4!Cw0y@%t!;I0V>k;P~bK4VGu?A?zB7=tR?nUdnst?o;k% zrLSiyVv`Wd!U(*2g4Jd*6PE!xy0S();mlM!o?yuiQSj`i?zVF0srU4D3pQ4%@@1l4 zj|z)1$;%ePUc=?0QHZL(#Mge5^=<|`ujEk8U!9!Dj@Tuv zz*y-UZBY}7O}Xh|K!zNd0Ok2%C;UQ5sK@=oHPnp8T5l7NHE?TSEve(j5_G*X-XEo7 zu=GN{3(oW7X)|+mx=@~yQJ0pgwCR!52{b6d7AI5y$4Fsb^$6xI&#MAFrJF{HY7oJF zI+s+78Ulf&MnY~5tRs=ALek~A6C6X6^q zPPXMJ!PQcEXN?8NfJpA51CrcwaR0z_eEA6CZUEsmt>)>`k@-J}S_;m~joz%jGdBgx zj=zp5jaHAxI#HM@AtsG#j+0Pd@KX8;6GxOCZD3i7D9oAbD#eS3v7#n;$O0H%GVEI+ zZX3I=X4TG=9RKcgs^p0Y-D?NEuER~Rw+s_=_$T*MXc;oK9)dpow$Qt;Kgs$M%)y>Q zc*+8bGj_eR3)3_#+8FS|88bWHv}R@&Xz*G1udh}$5M#=XUdj|G=; z8s3Vuo^)s3)11k$`U+Xg&zD+g)Fjm@FP^=1|Mo5^(}M=-teVcEi)1E8?frrG`~;^O|PaO?LN@O<7))%&&yzID8vs`*ZM z<9GiAD*IP{AI78ClW9%y;}`?8CAKM7pE{VvVBg}eA&+vSS~GgwC9DC$R{H}N2q8dx z0RqDzo$UCrvvK3Q<}ti}peDlFVVk#CpolX22e1F(OQm=Es0v@3oKpO7mv! z)AztcG0nhs9=O6WK_%j-REsI5;GJgaJ?0v(PlRQZ9}ksGc=ELtJyh`1R|Ny_9+wWK znZE}Sj@xuS)h=K8?q3+H?jqJGb0^>erN*KJI=FW;P0UJ}Q3bwCF460k02ny)3X0 z9pay*O}nGJ@!Iy@dDfxnmTn38cy#vNN+fYcaX4!lTfnBGd!zQGoTd8&p?8`F{`Oqu z1(`H+pYD z&*52u2_}`A8!IGtwPY~AsBy;J;jUpj4)rL4x1e)BRD9^N6?AjeP=$%6J%4?B%VHKk zHCFFhYooDz%#!C_J(AMIM-$Snu<$uG!=rTiq##Z%gori&fx~ksCE{teL@&OM+R}d& zzzG65Mdc{N_wgStgmAyoMTxC0Q_STC^S)q>pBe9t>z;nKR0UO6-;iMg1{7`cMY7izck6cPf{?gkA>z+9U1!H+^2nI6Tx zC<*U*^Y6dr9rifx&6{Jx0&iLq1opH&7Og|B+oSk4hi7(EewVb=Ji11m7fF``1g%okg&%j&X3aL-Z`nG#4S&nUWuOM`W7^DE|ePbr>cjHpY#UYPj(M1=WQF-wMl zy8)5`jB(^ek?3POJv0o%WIbRIf${mWNjWJs@;vp-3&AT{48)6&gW)Zy){+vQzx~Uyie=!PmAIR z!dlClKj@{#?=b0{5%Em^vXZO}Ep{;xy(u4+nhT`ZT0oM)NqCz*qf4}~FFaGQGb@m3 zIt03`&6NUVI{aq$<}>o=x$eRXGEY`TurI+N`QGc9)%EOZSWc zeC0lHr8jC(kkI=~mniHkyEkQdhYA~RJ{xjMHX5J8FAahQVx<9QbdNb{P&r0r2i?V| z1Fq}X%wAxp7?3Cr59C!K-gz7D@#(sb7Ex&vFH`W0FNP zhQzX)3mNlUSTJuofU0I%;PZr(WRxn8iXcgzP08TL1%=6q1y5I;5KG+jmY!VwBv%u` zKYrEpVlCgcx}>TI9fQmdRu-gqe;%G*{%&rZygUH$%PFeA55SU^z1rtfORyKpKZ)c z##k&2GXNeG3n*cJ7>xKTlw)Es)Cjh`JBqSO1`jFLCTznzC|H@v#V6+^M!JMTS- zD6IB0LB>(uHQ-oTYfLR?0jwBnyGw0EECzmW9*%#0SMDtd%-?-S+t4=c3P|%;^P|W= zgnID^^Y!t+_BY3GE)G6EZ$9I%ndCE;7)=ALmRF8kvE<+%$mA+f)sJEHHiPUll`cH8 zeorS73F+(QrjFrAPR#=pN%9>DxbuNHHbiNCpLpDCASEh!r^%#kf~!)k z4^DzilC|zL4)o!+?t!g`u2j}=wy$3Ay1_)JQ4wt;gVFx zyy#j}B-;HE@BMcjzH8;va}}BQ@JggZc0m-uFz6|DgN>xQuC`g$Cbw4)CkKPW-~Zs| zj_N*we<%SvSKx>M4{2cMW&`7<8p^d!`S)V6I z_HsPj>=MU~b2Z57(uGb4D{d9PXA-ROecj4+r{yo z)%2?0reIxfu(Ab}(o)BYsReho?-1K~Y>zY9gpF_eCeX+ty`bV4<7k|CkQkEx>As{K z^l6QTdg=r9&d~*YPp!`|_4E&|RK=^H6iP=SOSnHTFCJkxpTMgF55IsP4X}%whgay2 zX+4sE&j;Y;{B^PHfRhShjL#Y;V+W+0c0KV)q^W2;;2Y4rl#guu% zggZhY_1SOTdm}y^Z}Z=*y%QxADC0X7h&F@Q83tW>%@ofl1eYg*`>VkKZzjiagr2mP z<58oGI7)YV)D<^#hxxF<7X z`hQ~sbEx7T&^H{_hpb0u0-b0RO69tUqy0r!T_2+^jm*@nhi5{q;1Wue5W__n*F)aPeW6`@ISiCv3II??()s2El6_x3gua-PCR%dtK|@5TGLjqDKKeNSn->kxkz zmi_XcdrwS$;G9wB4FufECYWrxYjk|Au4@G1Ugg?wDVBAXzg?=jgH33b%N!YlT*WC?g#sNd;JOhzcwa!hUR#!RY@1^x?_{$h1v;9QjyK~ z=&^UZljG_ZeiMEm7bce#RuuVvDu(MXxfZ*SI^|P~VS3T69~uyTmq zw0FqS&V}kdz_x`P^3ZV=|OA=)X6K>qwvmyceSrJ)=eS{q%VujK-7HnsfH;M{Tfj!s- zm#UB9$U7k5z!OFc>p*g^s|3M?q6=cqgJ1-)h@x}V!iQHfjv4tPa=D6Up4COBCSl_{ zPG|#&o>(feSux&@LO54ahGX;~&y(%_TY9`aKVQcrXvQ2rp45PJ>P%ay-$zfZYd3U; zPSa@j1;d`m8oN{e{lZcQ5S`3q_x}#J1wcX_Imt&5HaN(v^t}tc9NPDO9(@wYeLd0- z^E7F=Tu9*CNl|0}Zu#rmvk0J=f81Gl_l8%<7s4aO#y0v_GeM5GQ2^u9ymD($FjfwL zdD^lp)9f*N!=9Rhu{6nsqT?h35&NUz%oPE0je#>%D0;8aRvJ0S^)3qVnl~7 zW03eQgOcs2ZgVMNnq_i511r6!rCZWbpyva$s?$~5(wvc-t3NL*9K`Y#ZLZ=eArACj zrMsE<=z?G_DA`dkv$SQ{d=9)5EXdqlSXNSe9I4B8Tt>dRIX6(R_rvWYVEOS z+4Qoyo5l*k=1HLOr?SgLfz))rRJ5Cdbks?fjFF39*qt#^*BTxI7IvT8B4?Bt9g^-Z z3G);SBf6nW^I8YZXnUg;+fZ8!@#g>E!09r~;cgT1>WWt4huVse+J73lv%U{IkfBm0RCOE z4E8C@((hL!%}bSo9Q;LX*^UgE#;0u9^#k(WW*@&9kOu-BDWD>cZ;;(y_uIsKfar-=i93 z0PV;+y?e+V?h%t@NmPLlM7JDogf=yCqV6q|CNB*+9D*%-qwZMopF;(n%#tk| zB}*-qC8a)gfcgWyi`pFL!xFj;mS69{k+&-MVk&Temfx!iQCMIQ(lP_#dD%f%9o!8a ze3HGPrzW}CR58nwIvgw?EZ-SNWSLd0v%a#p3dVu zb{suJPTG5^^{Nj-lQ;k1g^Kv@p;7I#rV;pHekvZ3`jJE@9O8!1uTFZGtA%f33hY5_ zl__P=Md?P7dVr$iKGoCG_1eNS)C&_!=_D7?Qa9QjQ^W2>N}Z3sUj@U`2YusaRi}qq z*6K8{W9Ui*TizHV3fPK=fx-R4K>zc{r{(Q$Z+G{) zclW24yZ5_&@a1J~|I>EgPQMp}z_;P|bsGx4t0Qe_GvdE+S#H`=ajro8X>3L3+!sV^2tE>+A;0Gd-MioDo| z(Xdz1bNlVV+G!^EAp%;Txhv3)H;5n9u0%?V;hC^!ouX*C1db_6y4OF#)`1N6J)}*1 z2-+zSfmOyrA6k@x8oOF9u&R5jiTRk^b?gcv2mKaKlh<&=%)9bNiJ)zZ(ue=r@zMg( zuecS=5B%<2Y@m+F&#PP-8PoB=!QbQfA8LvL-g` zDgP|N1IXKhV7JAB=-Jw|TO=n#_XF@)mt+bPrrn(8Hlh7r@yruNHV6{}PfwvDNa1${NA5KuH zzuL~=5(rqwdwD8-*txd|^*+=Tks_lh;Qul>`&O@dim^Rw_{7TJJE!#V@MQnC%p)yM znx8F)Mcwj6F};oF$uiHNEHVX9W1epPfURNXsp$KN>LpO7vmqj9G56nq6zATWgY*Dg zz^g%JJoU3jYstA1nf@kyr3->RV0w+Ci#(#O)5q9H*&Og0fnH}b)W;nmk4N%{uN?|qQwFmN%Pk`!pmjSoT#HN8!G5# zBgl~#P0XrDDJRY;!ZCdJqblA_A^!fm;;VYwaqm8@LCUhc5=9ZzpM{hev;`Tf=9#BRhS?jjP`Z^oM^CJv1~c9nol@Ou*7e~^79 z87GZxaDv3`@Ah1!w)JS z^z*HAJ?1I0;c;Tr+p)=-&;hK`G+p_tP|dNPwEiULB<#=dMK)ZOT>jL9p5T~Q=sT9O zcF+D!?$ZffE`n8=vW^2zTUCy{%Ek>dQChvQ#O~bK?#f_!W=m}B1f^8fPI&epFGoQM zM@c5w5>16djdn+A5r2-m_8Pkt2C1bgPJbz>-KNo|O=-%D&GC@xCSU)C z*fMER>tJ>+;*R}3tIn`c#AV@^H)a)-i(xt`-E5$5FNpB-l0!orE1=l;K+U92&&E!- znnT;$%QV1>nQ$Nt{0}6@fWpNUdNpoD#pRutzR|*!Jc3*x;97$pTE*g`Cq;A^R5bUu zX_#?4&5%t~{n3yD5#IV(PY+ZuRLfI(hNw1Sa1Q$)2A&z2-O>Gxf$LDDNgr9S9d7d8 z{Aiz<4`Bw%-v^YXI#jRDv|za98(IPNM)bTW{xKMd8|>uC_VlFR|NHQ5_SH&$pY=^$ zspdC`^djXUm~0BS<~INe4sY1q;unIgC`iQ}`7?z1KhiHgZE-fOw&t88flMuL;jXss z*64EL%A;A2n^mW*9477c$h4%YpGWoQ52Z|RT6$D;Ranf^$A-zHl^2U`|Ks{0v0HYv zy0Teo54s{J9;T85E-$?1Z@+Db#gxpDl>Cr3dSaI}jWy=Q)Kwz*?R-7L<$t#msC)^Y z3zG=q(6a{rpF^=}&HrofVxL9cuWPNnI6bpaT)lhx533lwnP@$RH14eVL?)9QqdCA5 zacNUUr>=ilbA`^)BDfhcqSlIVAcx zWY~IOSge1?(dv%#uYkNd)8didQB;=JW9~gV`yILmd?j+3_=42j2?339j_l!RmNjMQ z=eLMiBsY-4@?kbQo(Lfo<_#a2wwFljsG+X~O5_G)tyZGt%4nnON>Y_LfBq(bHV?r8 zPp|f3y4%;;$maYA@oE8ObUJ>HJVKt`jHm{Jg~a_H>fYwu%fuA>rE+rCG7)^16^$YR z7~g#kVqfI5ZzcwULn?+UxL^z|zktY4kk&fKDg&4-`<;GZ?aclM-aQ=&4u-a%h}e6K zNC_g5K}-ZxyC{HIS6G`B0@4g-ED7jL_eYO$oPr?WC{Ge_)pPy1W#4_xwe|cQ=Kx}y zep+OyNIz)qW!fo&(mYb4tj!TXcl7gte=lip#I0`#3lr6zeJ^w-4H~2Z<&AH!U0_s0 z?zG8*2Gw|&k$XX`rX2y>Sc-Z17)%tDLL>D0^uYAXJQhi1p*oxFf z-o=RpibW53(ILp##k6tF5n=&W%{<4DA|;BJQ_R9xCP<_5)dUiBzb4h#IVK$g%VtID zZLO2y`_e!j^l|4ATmdI)@>v|kzI3*aUj|bXDS8t z!D37{>*(@vmia2`IH$Clg^G7-fk>xJA$$I@rPWUQXiZ^KG6hw;$;Jnx3T+W{k-)U( z+*1CC^!=5F@Q*;XiE~R~n1~Cm-IVZ&S8*eP>`fs8wcq8TXrNI!WLv#r4ZvciV)Ax< z6$F-ClaQBWjbFUn(XOnWi*L$m`4hf?e5WF?XzEu1%w4>|-(_rK;}Qg1O7oUacdR@1 znp}Y%tV%XvN-1-5^Q@gExdH4X-l4H^OE_qVI8`f1kST2oTt$lU zEGelt$y};}t)NrX7-LOR?6^ux)&j)2Vsf#uk@$ixrLc%)(Lhlc7Z|812>pOFoQ!zA z91yPzcor;fG34nKky=BM=o4IWw00g)7BZc0$y)`YAI^=yrzZ2JDb4$zrTI;5rOei?cy7ijO&9{hzI z{j@8jK6Zco3~b?LkJOX$)zL3gO|EXH>7;ahsC2jiIy444i^$p@LsD$aMRGd3m7|MR zkFrP4k9n@n1BS2nFj~D;>VBz%RpSbx6g@28RxT?d-Q@@URw<7#PDSEsl0+`;|GG~m z0g{mozxeMe8_f&88lc86ea-ShpuM2(80u0aekVavw5ee$aiETC|1VT&^-Oy%&<4I>_u~Q> z(>SVEW)HsR)rUW%sQ7{Vs z?qbm?>eC$@@aU-o;ZtR?b%y28G(9vk7VBnIG8q}{Q-qY6B#m$sY?+ zu-H{W>e&%EGmt!?j$oJ@%(~i%3VrweFB9t%N^5Y5+DTTYfGb(9$Gl=^bd7*dU!#B0??X*ImQaC}?hOjt;!n{D>iJ_>$ z?Z(+aQi^(w0_9M2sHqi_EJ@J2(^!RLc;*`D2nw)6!8H`T)O0cNvQq2VXNwxPa4ZSK zu%+n=+7>6)2hNZ7LXezl0+tZ42rz~lm4u5jkQQjZCoUnT*|QMR;M5^H;Bm**n+}Yn zUbsdqAb~Cws}7KkjBWu-U5jbloaV*V&pX|**IZQEE^5~u&B=V4c z9j?We5;eI?)4^!&1GQbq1Vh`BX^6Ve5*rmC`MZCKRbU#O4GCzVXAoXn)jX;D14ims zjMBpW+pWK#c)^zrld+P$B{Bn$^{mpQx4^l^s6 z0;2ar{K}Ro5+_>8n-+Y7gf};A^mAro;Y2jhNjDv;ZLKqKw0_#_DNSfiN#rkRgGn`8F`Uf`9(0Uw0q22%(t&W-Kd*tTukwrwXp zdB3TtnO{@2s;leN>Fzpxp7ZRz*V-`dg=4~4K_O3<3ltKhl2DQ3%hvT(pBM(tm!xWG z>xH(7Bf8*8tu>rn?$x0PLi`X@9)kO+i_s-mi025rdc7}#UBOBMcSxXM%c-$5%C z8Bb0Kqyo|aF_$=u141|vCR$(J?LCcV{^(+MxU`ub1-}87&AHkc3pW=8ruayJKhedB zp*paP62>4^#`gBW;_AO1R#HU^FgYkAf8(~+RwDaOPd@*;JG54DoN93aj!4X2awjF3 zLI;`5NW%0|as?lc&#L(ZeYaYEO7@0NZclNL!Rx0?~c6o^G|81x0{RbDzKs)IOH$Sc;3SNSBQCIk5cjq6Z)NgNa+ zxTwWJ?W<{F&2w$P3ycd$8;!ykG-~RCLXpb6;75og`8Ucb>ZR2XU7_-(2fjKagF}E@ z3bbN&3aLuI(mNp;g7({PbO+brWf8|4yl&dK7MpNY5gY`L1`2c;mB$>fn_j6)d`&33 zCsOt>ZnK-4f7&Xo9q+G^tf5}}8=cBHjxxF3s3}>z{A){W;jf-E3a-Em9X;1KYW0An zC3Pe^#?ylUO2%GkAou_#?=*Ffj~FSg{S_cxWPt#o%#XXTPaQVRkdz=tqsI7b1=;J> zJeIyzk4K}YXUap~XRc0Dxx=j{G}2ug2prLTrcdXXnlj=*$iqboQSDZo=dex|1$hnx2BqO&U~qUM0Y8!S z{8xW_=DAUT#H})Ua7fbVpo=NqX%x>@8V=_p{*E7#U)0#3Wh^0T_sf;*)0(Dh{iTE>;6pqB|V0S)_XK6E5O7L z1fHgt1A~D_0_7pKn_K7ru?s5`A1c%O5YN(14y};-(@j6fXo!HPEh{-V+M3mH!rjIN zoPauCkJ?^BR%C8oLiQKN&zy|BkkYKaCQaV(=J7f&Uj+Dpos?LBS%6o7nVFxOlr(8= zn<;%>r9RQy+_W56PYcOHBD^sI^ zRE45S;v9(|rDlbd$>=^~+U`wP`HC`85j&bnxGafY8W|%Y@IBZ=5{$K40~>p*0X1Qs zwEqD-b!k|G_PgdH1jWM`8;vqJZ#rU-S8Dmx-2DscF8bx!5Z}<-p&X= zD@%jw{NYRG1Fq}`uk7%=+LQ>l)E$FMq1%vPPZ9V)05W6nj~$WudG3>^t&?V1J1opq zUM>$OCmSyd7Y7@&sml^r1!Kt3O6I`89H)@*FwwEM6{w|Ci8B@%t1P_CRlMA0Ca=vz z^h&L|ekT~_PLfWx-`m7DAFY%8HbeBZU=?<8oYE>JGXkdyFrf%j-VAA4D2iWrSY{C_ ze~}A=x~-m5LK2dNBtC5Vx5*O>g3m+tbP1;qArjxOy;|lHR$fR3gwHJU`!B_hxTHN@ z^O)%6rqOx^Cpke&)E*VjBWFD02f=O6!Z`UCIP@z$@|d{=hh{kLm&5nJ$YU!o{jB`` zBk@2gTnX@#e{Gi89jcn4Y6QAYJh<$9kb%GlN$?g{wvs)3BC)U#S-mvvcG6=Nm~Ag~ zEcP0~w(q=Y(Q63iMX|+0&t`7SRfW|xTA~4Bv|sb$H3^-3uH!f&himX=e*U$QWKukY zQq$K|Vgu4<pc6|zIfs50Xn2V+X(_3Qqkd6BEe}ZP! zuyznN;YBEA2B6!18x^cE@X+<^$*XoMY#$&ON!-=>1aqX-Gb>b)^|jge0P^ z?*l%8{D*;`X7FlI&p#fY`@w~wLBjHdu_VaW<+6H`?K$8v^Er)nOh4nfAd2jx^sz7WsoVl%2@Ek-3BV4*IC!bXoY8>+GwV4@-bK_WRUohsvN!m>h*M<=J86lC zs7v`ZT5rdY@REdMxbC~|KX6eS#3l|lUIsrF4i+v>CVAw&h_sR-I`$@;1VT2F7Ftf+ zMa40Yl2Ugm%800xb((uPrSs1ZT72OFb8>`Eo49x&D~YYX=D%XQ)v<4a&CaH?I_Pza zdLRvB(UZ{8$w|rJ{T`4AYeY@7|v^51Vcq4ym>Lols)IPJBjd= zb)`>myroZ;mP^o-wl9P@45Yg^^!jp`yZ*%7WP^}Q7s>EB%%2vjxR2+;3#u){z`%G2 zVzwH_9mV@bR_Jbp5WI!Y!=Mougvj7qJ4TUwa{su2cuZ8;50-IphW!U5KcBRcWz zujo|D9gchB&}9D!`-YhpHQo=PGs7Zszw_%a7<>i9BMW@aM0jQnoN^%o z4n*Wdtl+-y;W{4-0@aerD;VJv-NAR+l{)nqlPTD#Svyo^tVwz-o`$aOe!%bR^duO7 zo7d9kXFBK?28DXy7y7m@s5Yz0{GKyQ!ZRF(PdRH8MWJ-kVq&LSH9CIQiu{fNRMe|h ze8j4XiY_!sMY+}|MBeeNV(w^mmtr!B`C-NnBdyF*++-UwsP7H|1RggFL#2KB6r?E3 zS1sB6xssnoFm&Ik3~G1ew?XCj8QQF@{9n8^gGvtItuvV{fsiSHa3DQop!8(l&Hq9N zf?Bkv{`JGUpV1?g1d|0_Cm*f-Q?yFzBbEJjreHim2e6V%5`SBGXca1cYYrZKV@!oh zVTLXxA+0mO!(>DC>6-7!$;fOrk_d+IA;eQBrc@%Oe<~BJ+x~SV>a!%KmSUO?0TClW zwpUOk}X&>Nb$@2`XoK`)z)^9ax!+~oN>y7d0PN;a;(_)mnH4d9S2ae?^d(R;xezXCe$~A80R?*w~uyHvflZGIl76F#|k#zA7oPj<|R`; z8N;a5p_G2YH=6RQfj31lk6$}qem~*ciD31MM!g_&w{L`1hmu_vZ_tBFGHL ziB#@?hjq@>T@Dpd$>6s`zSs`lRlq;dFhQ8-MTDf+;gA?m?@)n5xZ>>xktxQD*{eQR za{}Mnp+DbDA?JKl_6_d$zEUt`g7Ye7!rb#lL0pu^pQ!@1_E;w^VqnZ8@SsN) z-8`sic7(#i;(aw7VCgVr0z8s85wvlNyTxECv`U{V8j_~*CveKo7YIt1IAjH}NcwQ5 z7DlDK|BV4ngWUdm8!3Zak)Jn|MZkXoV1AHu2=N3?LpSjJ08aDxKX5@TfF=-X)|6iCJ+?qg@q@<-}rEE7C_}y(K zv6G~6)=gxmv|3(+rb}y6yyd6yHz`lb*jOHKH9rYjm{L3lEq_V>WAxP)A7OOndFAIG zeFA5E_#c?$)Whjh1OSGxO-wv#2=w#SP7SVadq_4UhQ-3}diwi)UT@Ivxh#ooZrt~H z45&c<%xar~WetTMq4;aXON&!UkNMXnKy({t(ECI8qp!qM=7Rudx{!vfT&i^0%8yu> zx+S|X@Rt|ZlHaoQ;q%+@#1Ob&#Qvg^GF_i9G4#?xJAds)G%l2dLP|iM7iN}Ah9xob|@w5xvYyrgPcq?M1YRP_Yvj^Ugl~x)u1DE+hsNs#(=}O4!9I% zLuIGOY%%o8ZMn@ZtE_a_C)3RTZLSS)IZZ^Pi}fe7S|Qxz?S@Yma@0*NxTGxEl5eTG za6?Hp9I5S~m}d^-vL#(>fq=f%9VX2-nN1lH|KK*67yppvEZT(b<3w(-Yck4x4+8rM zW*Zo=CuNgNgXt5e*O|uUMmY9pX4s=J>a~-Mi_=xOO%d(>%BCt{uPy_7;U#DhnLVxg zHzp+vz%UA~3Dfn3Q-BR#JXJmf6H(?m9OI7=kp97nlbjd1U1Vyv5M|R)0bY9qA}2O8 zpte~W2VagwZzh!}4Q6UKmJ)QEPa;@}WsH)(AW$1*Xv6Q$;TL5P`o`mK!C-`wI;+(& zO_1u;OwO0D`atK!CPU?#)gxe>1%0CrQtwz70-eATn5`z|zxGso*SEWwI0vv00#`G` z@r#`zH^pO4oof$_+v}u9|3W~G+OU_|--8~QX;`uHN^a&-IE_JI?!uO29zRdB3r0W^ ztEa44L0v`1ikFm1eNIk^c09!)QsqJ1@op3_p1s5K)?k3H77W(B=aa*r7}juJqO_aB z)@>jjJf)$oBMw_DEIL)d4Ko1ixRmq`V=cpJZLCQcza1PP8m~6vHYDb;sU3k2J0(g4 zr-Z!Um_Yb5`(Ud-K!&Y;KP zkpJ>TZSW|@Xmbu6oUa>B&*AEr^LIn*#n50~_BU(5KO!@KjOD?&x=2T~w*CsdGKGe6 z+ClBm)l?G+svwqCZowi0;T zNHMploHM;d$K1GR=Ex@#N)acxYd^?OU9vG@p`oWQzP|{pfR9^!&4tJ?!C(A40<>l%kM3w^jG{&7%;p` zh-j1#@ri_~FpalT-($klTT>k^PYhNLafd*k@13kXYC6cn2F;M_E4Tq{6s3IYygeeZW;UCY6i;Zoq?!jXE)91T)2W~IC0Q4mVd_g0z3SxgU(??BX10(-U;%>45pi2eWhMvHwEce(NFWwSd0@^N(3-4Z9&Ki7^T2^ zA_-w0XhH|8Bp9LqHDJ718vkEpdK+Vcpv93F_<1f>_S!R5{wOxA+$6g!bWpLcyfOs0 zvL_~aKFa(-_lB+;M{4nZ0W?{f9?-MOwNS|l^k<-C?9Y`70&avwm-xDSAfU_~wTbz7 ze}XdCj%9B=R!0!_g&&uuBu3)>|4^=yKf2J{rd~+vBg=9mC&l;ZvI1Cy6Tzqbea=ZyH3icU#EeJZ06Sl9gG0=1u$5=d{WCd3OYF2tE@y?~sSXf1 zvTmkG*{vbXRenCnl|bvTk7M9F3buLbF#2sI?yMRoN9d=Dv8=mBkCX9C3cZASHgcAhFL!wAY{onmkg*k?PK#$6nh-w5>x zifoYzbRL3ODP(u3-{GF%4~dL;Xlgzk!5-WBmz@|%aIDGQF7r@T$?A&Wg z7lKtHoec8AE$kND6=nts0c8b-wIoKA#y38cOQ6PMVpj%Qw&Ra3NidZsBTF3%d7}6q z6^G)H$PDO5#=+WN2l5IS!Rr0>hMa^sjx?u}Hv7Q?25Jt%DD;Gsi-uaKgy>8xaZ|&b z8)`nDt5N$B;{uNYy_xBshD>iQMxE4?r55mdy@l?-gY#qhRIF4ISsHkY3MuYSO5t$t z;QsMs)*3I*ZKxzLEKJ{Of`e2YR|S0HZtJYP_si{y=EekiUEsRaSbvp`z}ATb&yJK7 z&ECLqNf(KfBT?S}hL`J+la{UZi%&_g^fnURH3=;of;tc~1?hNr zhD!jn`*(1FGCJYEGHn<{jbSm+oPSYInIsG=9yXXMZ8dCTT{fZqL7OIB4r6~N=K#! zHPfIy-PcPugxa8*%m`?rDweF{V_fv-eIvwQ(ue|J5?pTp)JG4}KN&CJv*d`k!|}ZY zw7&<(opgj8I2b$S+~zu9fWUCt+5SlDG(IZ)RfVLRgX3+HDXuQUrXzq$EpfmkR{VBO zwa~W%-W&szhDpVLBF2Uxyo>=NJhIE51b<=OL`bHJuLl<>)o3+AQo{PI`kEDTy@K+2 zYw;?cTbPNSMIjdP!Aqe3Gs2T%(vgBep|*nQ_~7}O=svmwp#l{TN!{1AsY+Ebad0Q~ z?JPl?T@LL2B8aSP{6>h3I=4*u^G8xgKJ~0%Eo4jk<=viOxQpQT_Ba*5yqe|NGu{nMj7)qKlbh@NwR!Q&rT9)5NtA zJD*#nm^0&W*Lv{;z3OH511E}+1DBIjx&C>7t>Z&^&na95R6xQ074HA=Cj9=nwFSl` zVlRSwzXt|g^TUOl%^=9@LVCU!_kUFu7G=IahM20 zWMBb?4PPUXK7o^dPA3&U0u^5ni4q+n7L~fpuTGT@mp(jlu0>}Vw`alkT|*+6m1B|F z4RGMYaT5SCJ@r+oj2ME;Ny{bTpfea;Uy~4GbX*|Wsk!URAw~(=EHYA_rIm{9lyytp zJW0~DA*<(Ugbjapt+Bf%N%yxrF8znI)JO!6>hX2Hs3$&_%<%S%oDe!M9iE^wp<3pW z4xXVt3$A$S316n{DwWk3S2iLHafU@M5JX;fUGRzs5`xqwMpbkV%n^eXkW4+s#UoOX z7eWDv*ns+AHfBmI3j)2mu_wHmU6N>Agd+C+m3j<*E4e z%D95zJGjtS^+Rcw|5d84Xk89psJc75g80%gTBNHxjfhaeMM_{^;1-BgPVYCII2+F) zN~$eGcu@!JEVqT|X5tmwt+6cprDA=joyIOVN7gz#Up4O~t^27a15=)Ep2*lpFAXG} z8e}JywuMtdczd65sCddBBOX{IFdCH7_kh8|Yw2`pp`v->M*$2Eh>X7eKB?RYomeiJ z_NVKlr0k5O^dzI90-2vh%}t_kuc|ZiIEjoi7-bF9|6ABeDjI2hX*}c8L;@N?eM|_O zku5w5Gz$v~83_r@C9H(LuQ$}+5E5t{pdx!Rus^aqtI!Jzkmc_cHs@=;`yLeSmD#Zt zB*xslp7E18`M9AR_@-T-`=+ItKLIEcb6A6z3zRTFAE4B=xYL;DBZ+>RVQcj_t{ z`{aUe@edKz_CbGsp; zVUENJ5}4R4FdUh`C1=_G%`IAj7BISlPx1X?rM%r=AFWG|B68La!0^bPV zFJ_kkj7LF?>ayQ%gC8>MEw%2hlc7Tj=avQ6^%2e19!CU3JM_7~I^c3FdQBeSs0PD* z&?aQXphLOdj&|905n1TWS%IZdW<`Y}jXnJbJtIlc3W$t`(|t=;+JToj^8Ue-J4}kT zI0K91J|?UOaL}*Nk%1BWyW1A91NSvs0f9C!j))a{0S1DJ-3{Om>Z-5;Ve(1T4-MC4t8xY9hJ10BOs@!C+aR>z4u9y zECD7cVyU0gAg`|@%sdi_7>oH$I#Ad>_q&`0l?jvH2{l&jDwt=hez|W$+0u?7zCE)k z)1MxnBnDm1hKpcDq@JW!E0dfM!-mWAqL3v)A4JId&@BnHa4{9fxmag) zqaK#Z;(q(Sh62{*NEb3T0!n!!pOqJW5^i;La=P$m(n<_eR$$u@2DZH!B{@B*K(Y&G zzcMT4cXqtoyi@;>>0Q{*U?(_ky1S`UbWxfv1({)(Ut5l@s@W{^Fo%DCYRg5k|77-$ zT9Tp2l8kfF%C%;@X{MOyl!!||N6(N-eII;&_B{FuejaQCC?$LVxER;GfSR}yYF@z0 z8{k#pZsJ)=&&T}wK5Fv>^?=;P})_QzL4rFaIw zMTXtJ0n|nh5&??YfLO)Z)UMo@HXvz1g!`Sqa;5*6C~b*t!5>Ch&NM9upy-lx^IaoC z&@s_OsSYj0YA(E$SYH%xEQJ96HhC+2f5b#Ca}mtFhBhR}v;8l^`JC6Ci5Dw|^S0fTyEe?hf z*Q_WYVZuPGvn4gTo*mG4&q3AeD zp>;RATqI)P2xYM9meX8<+_YKh3L;&6f-Q<9Bl3{nxEE(QS*k`94yX!sF9B?jX?0qG zVz#*K-b;}G@x2LoB?HhP=Tt~JDaOA+lE8RC6-1(fYW zjkd@>qT4rGHB{J|Va@L8?6+x*KQ`vl^<>cwkEE1Oxeb!PW8wzkof% zQt*;W{u!~MgUPW;D6`r~gAoj*LURHCJq_ANB2pO@p)zHdyw{KoeyK4=+R#ZYZI9jUVXV_u`H`o#TRO)$dJs zT1Ck)s@BTE(_D`p?QzTGX%a|jWoyXLb4nk?FE&Ay4?o`xW#JFNXtVJrr5AwkCzkjT zA2t90n-{=$t~-n$!<3e3MX<{wHNRD8ihPfRqmgXXu5Tu`C zj%B0f+P&$HXk*L~a&iu2s8AmHQn{h0Ng5e18k0@Uztnx_34S+~ByMko%X;MG{pjhI z0mpdFqK#2#KmMikM}h(_*C#Uv)7fv>L;{Ywjqy3!U%r^fEXi{996N(>tc!PLSu%(p ziz2`%dvg9zn9~RsJtC!M_R~bdWpYq|4~0dBjC4G$P_CB&v^+9+0BXAaP=GEmVwW=; zkts;bQNAYz$oWV8R}K(##HD<`mOi05BEgFCJ;?TvUR!1(UWzgdsBBUUE`Fx|)=7R* z7VeBTwisTgeNfd3s__BnN0i$$q|-Xf>fWpFUvzB{UeGR1iqwo1#XS+ZdK{GD%}MPf zl`~5wdZS5trlaOz#@cj8XNk?II(CsWI2c}Mdvq#OeV(M~Q+Xo zISeo8up`H%1xL{^Jq#9V7$nki_jCy*Lda>@s?2{`wKg1yF}7!UYhjrGh$!%ZL{os< z5(6mro0B;1GwJyb_^B5px;w2+9M6%STg|!_59+>O`pGuCzT8`-0Fn|CACEsmj=&dS z@eH7Xod~E%1>VMXhE3Hd`aM?Pc;hy&?u4*JVB%7bf)&xRr=gi3W9z*cGmYDwp3-6bT}YVe@D8y1dA zBp{7yM+!qI76vh6xOR|JtPtJCo0xdzXLuj>9K|srcllh4sC^qhwFKv)0SVVH`r)ky z1CqcP4=y2Ud$&j6>Dv#gNaZabNY`Gj-v%A{CwDa%o;5VE-7c^_GuM?9C(aXnf4|pw z9{#i$3?w({a=RYGqrD;u!Z1( zf1Ce)*{^5-#Ki)DiBBB^KtWzwJ^^SG$J&3rfNcVjs3UUi$;ljtkCE4N@HfG2tggeJ zPw?RO%H|_7KQ(c4=Yo%oxPT|R;W+OF>F6Y)0Qhqp{XcBRq4G&yl0;rHkuXskm(e^` zBUQ1oQd}V$41OLZ`x{0=zkKHvUDbO|pwX4Lsleu^qGE*NWqcvW*+c={%EQmaQ^-UC zkdZO)5B>qr?_M8Dky`PEj9Az*gg~mg&|#8gij@50;<}mQ{CmR=-l$SWY!{q~7j2OB z1hUrrF#~ls(-c91{d+}f+{mOWgmDVygc*KPbvH$TH${Nx1NwED;u?{_Yq@B{92a92 zENx%L50Ug*y-LBERlv$g(`}B4+mr^vDx(C}l==5nn?m-oyiR_tKXbYu+M%O>puzb0 z%{KV!>-LI-pnt}*2+gu`VlL%{A(JhDlkEnlS1~u}8t8C9j*!b7J3*Bj-q@^MDEhbJC);X8?prR(zP&h zdq)cjkS>vwHiAbYjXA2LB^tCe9YrS@BBT153+V_ug*XC{C^Z_Eeno&%Z<(pf(vA#v zFc1UUByz_RMk@vdH*~)*Bn7oHfThqE7soY=2?8^AA`PD4-y>^SI=^L4Oe_om5>Xq6 zM&&QYOhg>8k1?-R2|?WklVMNKmR}3R;>tt>j&!(>A%}>B%br9=Kbej^;@sk9N;P_X zE-Ex1WkOcYjseOqa~{P7-GIJsysRZO5**(W2b9+O~WYTKAJO)t9S z7ZZ`Nq!ZS0y&Th<)&@MvPbQQHGm$d?A~D!lBi}_j479l}5)u4L;-!P(g!0A;a2^tA zHY7^~a~9lj8!9NecGOub&MfyEjYAH0k~-dDKA3cwi$pM4NYG?Pn0ZQ>BQTj%@KI!t zLeY!9TRIhwm@tTF)bpW}r>?f5~ZpTV_>tY`Tz_g)677t6FE?RCD+z5FlPS5X$j;s zBD3D7PN)HHPSSeo}5sC zH84dBtx%0n6Y82-K^L(Kbqzs10U7_7c@{;^{!8>sz}Xpx_F6dZxQ_ZM>E z8Y2oYq>3gL0;iCM*vJT9IG`&fBnP=QZBgtc!Iq{RLy_q(gh&m>p3n$XA16hi#tO4S zScn|QT~4VClhC+c6xuo$hB=S_n<97o89mL?vnJ$m_Sowye zoFo@y?|hWWRapv3sSaOK=qLCLQ@+d$fHD#zt}vE5UI(3NO2m&Vw3L@coI#5qfHu!8 zREIFlOQrr7V}VqlZ8fGs{!`(c9toc`J)=}_$k9%GiFQOY=2Lvp-x;Mg(g+Q0gp>AG zB`)<3Dwp<}OZRfY;n3-xUZ=Dmio02QJPKn_1e-`g(GZ5EhrAfS#oTZXf=dQ29?z=_ zV#xT%<6~oC8s%*Vue11-t+gM?{^Rfb`LWFwIzy@RZCJxw)@86nr-9AMu`L zpVUo6Wq|=z=o{gq%k2rE7;g6M5$PD=h|QjX?9`eXLWZFyYf`@LvOTl1P<&!dq&@@5 zNQvY$y*CU+|1m(9G(nQa5(a4w8*J^Ax)f;7T7QRm%j}nwY8W>f8jey{0CLeI-k@|+ zf4FgXl<6ke8So$?W&-{LUkDet;a_b91qA+X_(?C= zpdkjv73-+)CM;wLJX!SdNcFPGMlFncq%sKVNs^>v{~1dg?!3vAb9eJB(-DYW1irWa z%C)K@7U%F3%u7=gTaeSM7y=SGcwn<$>|XEV=TuyuM+SdsMatbJ-WX;82vQfYO-fqMCymMuMLf173_81XSWf|n}}vNccL#?k*%@Kfu}l3_EtoM}E_btSrn zo>0|HXRClvF<7)791NAmS{ptT$-WU6s}`iz8Fx$<=RfJ{T6eH$4bFmZj!Oz|+3aa) z#fyZ0{i8AtBf~u;ZQY0DjObVn!eX0Q>XABQi2&`T+H&_RTXg-|Gf^OxNcnB|r@tIr za;^8i+`^@KTOvdsj!F#P5{ZtED|~M@Sg44E7`$}@X}EmjJ>Y1^JP}?EOHv|FszDHy ziCBxdcj{8^;FznUBCqGT_92;@W?GqP0z!u@E-u|WEPgOuHOlyc7-aEhKcAINF|MB0 zXwrgedX$+vqGQKJj^KvOFQH|1hD#b7y&7SqM^nfRi&vw1cMc_Nqbi21gM9efogLSrofU{{CTzR_Rq)#@BV?8UF=LNK^km{&RB5DGv-C9Vg+#lpq@r}2#*?8ul9Lk}ZE7=u?b-9bXP zwxtO@z@b6SgKr#qQ%Uu?SceFXO37Z9oX4CKNrB zkN6Yu%P_qZjr%j1F@4CCK#u!&hmSw^>XssEP+58w2BwgNkg%3X>b$&87DmO4`zLgV zld@SiO)-d_T`#f<@Xx*L#l}I<#~^vBl#O1-FEbO3CMj9~(PY#LGR7}JZGusQ zHpVT(T7#3)Rjn+U|AOhu2|@!ZRjZ`gFRs3-h|mDXfGdhv+60D;VC|P2%FcIa`#rBF zE!2QKC!8!zWRk4QqJQ;QvYd2r?{Y%7S3MEEB(Ppvkcwhy!iB_UAlgAnj8oJ?XS=Qg z;V)<$=@5*!F0fN3Hss;#zFt1{5{wsQxd`YISHLlNppXiwxc)&@H3j3W9{y*#D+b7_ zGjoK&x>9Spz8H(&J-@UKt`Mb&qj~J;?~OLZN8dq_y>h-VVGyI{Lz3fxIboRTc4Zz( zssga9!6FfJa3`^B%s^9)O8xM0X*M2bk+JHJG-n8oVKjg`;!ooH0fJ=F1yv||?D-H5 z-|eQ2V?@8anHn(CAl`Dr2ne^O9Rd+`u(2F|&``3fFcOwO9LlQUXsH+v+aCDkIECb6 zI`KsIpIQ@Wa_+kR%xU7ufOeSUV-frzP0rBZcf1P~Y3$tg*^~?GII|?2q+R>A-e&gb|PfAcLk>2|9O>`#pR= z?iSTn_j5Jq^v!^X$05I}+>@)uMFv;&nT3K9I~gbA_MDs@_eVc=+K;z4>H}l4>8Kex9!K_z`=k>u#TCBF__sS+P;W$gtfZ^1t* z)ONGS?m2?B`)fxte+e!5inIJW5K*Toy1naFJvQTbU5DKX0DKtFml=o85s8pI@*VS7 zmD@kEpH~6%gxihP$J2{yg#G8{`*BE7hbXCn-_zLcWHhU}mBP)|t83^-odMnJL5F@a zLe7xH#|qTdy<89UlqP#Dc{9?9q~2@we71D80Rq;>Ug%wkO(kFFu2SDzem|~#4glCq zOTTC)USolAlCYLyM9qb?*y|?YE^A#8uDa94E?MBECiJt`*-F*vmV@nU=>T&_PbF2y z+=qCtD*qR~>Pc5U#GCVW)X~*ShW%7l3Ag8$QerCM)%tS5)x=zAY@?vtp)Y{&bUVLg0bo;?fBbj1 z*PY7w9$?f#k*y@iH(z&nyw_py#k_yLQ=zDt{&h6=MJsXCbHATmv_{X7K4U@nq^gF$ z!*+g{0sAJ*osJ31z}8hFpx$H_U32}%_g(nmrDJ(=hS&R2DAG4#pEPw?;PX5dd2Y#D z7q{c6L2(05*0)0agjpMRr19%$k}x&l2E5Ae`#kN7SAYrMn#ICnugxCgRdKfJpYM@* zfpo~QUPG$IXU2S|Kv7ov>ypeBL)OMyf0uu{{7yuqk6SKB@!jxhZ9nXeE#Ocr_R0@u zA28cf9#*l#daPSD>+YN7ZN0k-@rSiEnO5OT@A3U}u=lRZF1m)<{&mWLLOwnB;!9%y zneg=__A6h~%}I3Wt<}sVRp0t~gSF<}5BV=ZVDC90hjQ@7mW%vMH2=NP^Uk2tu|>Pz z%4s4&;?7TR%?Gl%QgM=_>*M%jgL2xBw)pO6+eW-jYq_{=9;Kex{=H~tfAm(8lk+h% zW50vF@wq`Bjkv|d?}Pf3-)%NS&RDED!|OqBXtTRTeV+UMgpv7Og^12`X{RIv@k(A( zI$oV|f}pAM{%NpbvBY4N-t?f@ym{Y0ReDwPz517^TJEV_Z3NFli2n_B)WWGl%l*8!^U1H zuADMu@stRF-u$qs(pe(l|M*!6+B?d%Ovw^Ly8SQC>pym7YL2gvBJwAcW+i(zrx;K=}0?Uf~ z_1f2(9He7Au8XH%rXBq&NZ=eNzMY2rrtNHJTWewOvV+*$d(z<3riAc@!SiuAN6$Ab zJ;TpV_ah-@ATr@wsb{-z?)=bI*4Vi?I;O{`^nL8mtEBvo_D2Y(fcMeDr-d&;t=~%$ za^i~mHJ?LY-f#X6d_N9r3T(lTF6S?u7KBrmmYH4b$%Mxv zP=V8>j}YInsTg=(&d<~D5Yx|U;nd^hty|l{gd2N{A$wcAr=!vAe{zwbzuywxZF0X4 zZuGC1PZGaIY<$@7gm3aSc|NyWZl?85Vm}{?t@R4jEoi1*DGw!dSGtquwq8!)4NNM# zD`j%bDbabYy9L1~q9sGk?fDYxO~1beL4O)If0rEI^iaTixg0)ica?qp*;)RL{|xNgWe#dQLth6z- z-CtKQZ^~NR7vMXWuxlGkyHg!f%lA{V8k$!sW=2~(F@%#?#a0B~MvIN%S0ng29ADeb zg6o?XnE&SX{J0Tm;WvG5RwLJtUr^jl|D><=9Nn7LanBv7AuqXJprqhdKMBm;Pg9;~ z^r$}V|8v#xwQc9I7$`cFl>M&swb(lgfrnI`l4!|J{oE*jH+fhwkg;AgTk7una*pu< zX#cX+8SdiGqovd>ZO-34?U%shQ6Tu$vsP6QS*{OA*5P;zI;5SUyYw>L*$>Uqd?FcO zZ8$&dUU#-H;$Y9VaVDqP-F5nUQYI}zFnGOZ-Bg+_i3v0ywV_5;xlnpD!Mz^ zAy_?#wcdgEta*dv44XBvclr8k$Sqy26Sl7;!28k#C3j`FD|l<$x_$2iKVuZPtMGnF zZIj2zJXL$!JcWPVN5AqL>b?-TcX*)}##;7xHNZTcnf0mL#$v9UzKASV9R1omKJ3%i)cz2foV6i+6AMY4iv~ znmSqR_Oi*pN;uD7wnp`P{y1Vq3NG8Z+Kq*($LI;Jg9G0BUhRE9jg-_JpZtaauXbIM z;E?83nQ?+fYIYUOCjxSp?-Dz8OB7A8ZTfE>yA(@CUmrO$epgcllJXxpZx;s3=gV2? zf!KdBUi6sk_E!#DN^<_RI3k)IZ)XJIBE)RAJD(gu)IbCqrk-0+TvvBq+>F0DBYw|c zJ&aB`8}RXwH?K@;TQzl8Ec%RU+^QcYr?0$pe!tLKT`Ye`EXtQQ)vw*uY+dPE^u32E zVsur|WaD_8+r31ps%#5>=w!_%|BLkVCO-|e8dFEXjVhl%S$klGaW~LzCwblty|~8n zkXLPU|KmKGuy=l1yMJZ^+G01(zTMSC_W3#-ZxH+baGE2D6Qd94Q7i+!`zy$?W3>e+ zKF7U!CZ}c>8R5ffy~wdH13xIhA?SCDP2T~2!gYJrZ@6Rps&%vZ$Eo-|?{w3(nZGg` zAFU%ElXm|dmOFc4BJoDp=4BS>=f%G@`;B=)C#d!2-fA^YxkK)L9n5i>gaNo7pV??s zqu0n>m-^0dRGa%n*jO}C9@3+q4Y>IFy-|VnH{s5njbPj6@XCr(1c2q|=TP%n%v_-V zTlpYy*V*~H1?zQM+~>z9!ajlB;H*yUvb;&@I46|!;ru@BmUKn-IM#2_8~omQL7>CU zX}K9`=bdjp$*&2TIlqLj@&+h8c#~^#erNDM_LX_k?(6_*&e*rs|4N;HGQeW>Tc%Lq3>AHZ-Apm%_AHRLucXgf*A5(AO2%J8PZ1-K-(H@J3f@|`7U6rIXoU0pn zoOBdNrz{zGUq8h49P8sNd(I|694z)#e;CUPEhUr^8@m)|d zZTHQx*PD(Q7DAZ;zo}%_*C_MHx4UEa8lustXQul!?eU6tK=6$caF{2_I7EY!j*(5r zXRvv8@j&S7{-BEWO@Xsq>3D>$cVZv$N@Z){^rViuEx*YLuoyUMVqe~rSGnr;tGjW2 zor6uZ`>s^IhAr*#I>RjC2uxzk-g%IpWBrg*3_d`{^u=HOXW3DHXT!;{v~_!$Iu)y9YQA*l zSAU%?j<~}A5}3Z)C9wad3`;_}^=@j}JLAe|M)m!0 zkWN9IOo_qvdNk>t^NAMSvO}1@Fy(Sw)le}Pt}IJ?<26%jsvN3`1k{v=Nf0lW2g_3u&d*+E0G@;zm(^D{Jg3s+m6RsLE&j}qrv>k zr{^Me?WXC~Vk)9|N6qF|{{b-{Ko&}eQ*ZmHiXhi3<~+9PTTtQ6a;tZ7D%N~!bCtu_ zvZ!geQeq;f!qe2bqv)@V`-aBbTi;%1x2lst`v-~ThT-AWBtEbW#*Dy65Bb{FUw0f2 zl2$__>%0FI1Qh%0>!1C-{qmZ37OWfC|6y32zF&p>@7=~x!+$@qr1R>?9t=pPc#EQRbLu0{F|1F9hO)3?|yVMuQvE|X4dF#Tbm5~YvNDe zR_~vY)9a&Y(;HQ-RCsOIw92owh?us)PyKS}{?l)4{vx`@vX^R(*ctUOYuIN8<}drZ z)sAPYXYF2N>3MDT%)_&f1V6VTG4SYvt#M;dRGxL_^k!Ypfj?|qqe=aV*s^@%uC z%a@PyZ%}>W1ib!pFV$78xd6lNrq^4w40yqCug~Ibd0|(U zCamkc>eJqpbuYGlZ5aCSyCS$U=mHWp6i``kKGv@*3>#8mb8S6g-LaAeqk-8GjDIQDz%^9cv{ zKC6g7d-V1BAM3{jg!hV#eXDWQP`{U#C=;KBZ503e3c1qrTHzZb->6?TYSf1xPgAVx zx_!vyei5-}KPY@9BfV+j)Gyx}vOA{tsagAXrG7rUmicHK|JBJ~*Qu&qzIsTXup6_# zXg}nuCAaWx`JF!MXR00=u%XS;tR=O3r+po?_-^p8pH{ASvtyl~7QGT^YP3SRb?1=S zH`}aP);zHC{>o!c?7oHctTDLztIJ1zdSvP7kd52sTzPdz!o5A(zQ0{QGb%cDZ0|!m z-aP$|Dt-C-lM@gA8nmkZ@}z^$H5@;(*4VHf`h=+m7Y*+Hf$qo~)mQ$L&^&letKWa? zGaz6~yOl>n`Y&0vB0l5eY9CHJ@i7+N|9GYRcklc-eSYO$qgSn+x2aM8?r${joVg}{ z|KWvSF8%)O4b6x#snNB^u0MBa>SoQhL9=I9>C-E5Q_6)-Gb1Lw)b;h5?XC_sYnL8;4f^rph?t(1I$Do~>nQd8u0TT=SOQQ(pga@!g@7CU#%`_Rm=(GXoB8 z+VbTpb&n526Q&+3IlZxca+`(Zyyn>ruFUFt;^LKO`h|CE_rO5k^?-$3M`0cr~ zO5>0cRsYnE9UjhGHFeR-k_+=UWMGxsT|%bbzkBsT<)O{e9WATb^CH=)=&30YCNp z?(>#eWM#N#!wX$|)_?W>BWz*l_Ih=<|MB;xmk%kC`Yrk{&cF74{I}!o`$;}$IVH1nI8`Rw(D(82sXAi=R8W;K#$GH{f+zhmHt}-?=Ab(*40dj68Z~v1x7VYpd2*{rLRq z!B<~h9;mq5!SCnVdpfTs^OK`y-?$ZWDD&|2wm<$AFzf8`IP*W)AB5Ywy*8B^#?hyz;}MEzic_kEY=Mb8kMpbbMGzM*GTbj{ep0 zblXaO13N7BYc@KecKeVHpZO?)e%ea^a7Vc)NqTbw+jY5z43 z|Gu%V=7CXnt`r;Zt|-ZVCi~8&wf8@R_b;y4Q5);`SN@Kz$S-sI79K9xvIM4d`us?> z=x^{7vtrlzulnom?VY>7ZyuWi_1%9yH|Cw~2m7b)9=tDk#lkH|$t^=(*t@C!*$c-$ zs4|^DzKYG^&$K4-FI7EbJ3qXXd9_(*YD-AtfvU)cHI^+3T(ovdU$oDUoty80pSR2D z5w)%BTd_afZ2s+#=$PqW{qsk=KKaU>k>8!Kdo*jpfR_CRSB`%0%XHPi7Oy?HK6mci zef#ze#5djga9oQMUs~E+jkElAc*~Y8Cks!Xe764W+qZ9zys;~A!u`RTM#bvo=#$v*w{ zYhPCnR=%oOv+%=r3J#Q93v07u&BWnbU$52-IdN_6GyP(pt-rnAh&VKK^uv>9NTOit zrs-AR+_bdM@?Wxk%ql#2<@lQG+bg2!()E+-#nl|t^$+d$StD*8dH&sJsz?3Pt?2ggyztcdOOCIZ z_4$}8eXxe_UucB2p1Jo+%dihtCyaXrY2WgVM>!2o48N8Vg}j-ScJ}U9e{Yz)K6Xw} z==2uDuf+#%OP^b`vqsIrd8Upz)fCgK9<4O(kM-xCnH$oy#>N-FZhd;-C(VA%-aI_^ z19Z`IDJ{`Df4>qDq3u{}b8_COnx{4vN4_?`W{q|^7gA_T z7%=8j%gXbwzd5!>iy|<-ZYBN4i%0Fh-ThXB-(H-*vt_I6FRr@r@sXu{_mrHxR#eOX z(wQEW+Ps+e{Hl*feLk{RC--*kTmeSU*7no-r%6)2jk<%H_;qz*LY+q5&!A! zNrQC#y3dY!J8DV5gx2ph?9gf1$g^+WczfI68q41H>+@yZZ*GPx|N2Uw4;Sqm{b};1 zxiP2A+v^%XnmwfD{5P(?T7BTkJ~<;ET>o~_$5R$)*SDY0J9O3HlB??$O&!s2O#e}1 z&?#FcZ;2i~#^C>GhyTP5lgGS0d&}e#(-v(CkG-(z)yW4srte(S=6=m@zN=ZM()eTD z3wx+C)uym(vtQp(nXErG zxX*_h)r;R4aH{HxUc;iAuTp%tdGpJ)p8t7hSYU_!ew8jh_x?vEdvE6y7Jl*4G@|PC zpOaJT`n}b!QPH&N36uT+)4nhI?eM4T8^_hFvvVM}A?fARXZj^Icmbhr5s z)qapbwgq##pWddv`QTXoGZ%0Fg#S39>Z&5m>Xg0Xx1G7`_n^MI#@`F@v;P0+-nFlOy-QN54L5~rIKYebxHV+B-`<086{037=DS<;R zV~&hk^uotg`rJ<~*fW1`r9B^Rh}tsbr_Xz?NIfx>5tO}Rb7G%=;hz)rXTMl{a&u@_ z+>If>y}rIxlhmyK1+6uKdo0NLo+tP?oH7~ zKWjb>-+!UOrM$Vt)v=*1>YYlPQ02L;voH7Qt3Q9a$(UvDD4P!HqW0^epL;HQK=;1; z-t2QLO_h_fZTt3=y3I!){pC=^?LnzIA9lTc^sR{8ovBgZcKvPg{065-e0-*NlYYU~ z#y;Q5po+}dQ$2K6-zAbI zsv145q}vy@s`Z&1WW4=M>K1a|;SVyWAIo1iI&uB*<0ab$^ffGcE33hbE}e7UyZTAD zN}-$mu2n&=_x~#9rAp(6jW$QtTe9NpnqDhjnm76dqTg4;0w#U$-{aF`2Y$P>_27DH zTk{Rg-;QoDcy6D;4eAZN9TEO#^$!}ftA4J_%hmo|IJj40TC;T{1J1ok^dB|t@Wy&w z4sKdixi={v5(<}^Muy>h?xn%Vi|f@}3# zpMQDp{qvzO4GryIZ5|avV&?-E=JnY%;Kj%fj|ViL|G%_%I=#DpN827@OX0iA@0@<^ z74%rnxE}|Rb^BJohNnyqUU{NwpRRqV{-cuaRQq#i>n~2cHBP;})AGH~TmINSEx&ln zpsQH5=|=~T?%Dc-0)wjZx-1ZRBr?T6{Hs?MU5owQe^Ny9@)zE@GNoJHyu~eRZC*U0 z&E@bZb878cn;G}P!R8A;Zr-N$uDotXYvkWZnqHU4j-K7(%ixqR(&zqPT*8O9NknI@ z`EsI2VRER4(<*iO&ivlX^He)dyfgVxD!k*zslVUv>$_`ME`8&kbdUF}#nLC4y9!>- zRtj~SbUkCbQ%~8SZHK0^i`n;Gvy%)8_Xup~)BvL94xA1ut@JdCi`ht+zUfT~#Mmq>nw~ zap1#Zwy<5z$62O@2o?xs+bh_toS~i_%of>ssw}`Q;UlMU(VATsJEzQXdFO>gwJfnPBRY+iE%OS-q2y@bB=gl9SjHocosQT3ncroXMoo`Xowi z&56So{g<)jn4D$rUbjTjRk7o(bb*fXq6zo?El#_+rUi6cip{lo`tL-Yie%$dp>nVH zdGT)}6n`5`UUO)flhx6e5tqImc=u_E;bVq43B%o=@?<+-K7M-t=r_aH5n*gOyH>8} z+3;f7Y;q`TaZ~_Widzbm%>w1G=myGC8}-MqNedlap8oh zNQwY{sR#`lmR~ zSu>>8?5SItSQ>CL-^2aq@>Q>#XKnDF!Jy-3a$L*byj>?bqxZR@X5}uG@F!XeSD!k+ zrdwrOhenRGrK#cdP|gTe~DWPegC+kPSC>RB!fQkSR00960yuDc zVQyr3R8em|NM&qo0POwyciT3zD1h#t^;cl4`A*{Rnv&n?r?Yy`QEVmgu48-IPJ6n$ zdmV^`B-9ka0-zl=&HsKE1^@{@MfsKFtJ$?0iv$L*!C+=EkE>*k`};G@Wp9D!;eR~D zv$3(UvA44W|88t-6#w1a-ro3+&7Hly-IrT0ceb|wV`Fn`b7%WM(8h!Grtl;}V*Vc+ zcW$dVxNqdaQN$#cltp6DLCD7vhiuyOv6La{QP%efmvll&FhDWpG9tXPSU^P_;)NIC zIT@fIlR2&|rap@p`(%I==6PZvB$-zh9n5Cc(_?%UwWZVlD;?FBeZm9p~|Gi?-boPhpN(Ui54gaMIp@XbY z2cd+AmE8w`4!o=>H6`JqH)o+FysDXfD1hEUfrchRJi}ug;SBVg`ZG+z-k9KI&LUX_ z{T;m`=#?qZ0Nuc{pgImF$VE&B2*+_qeZbz%bsiJG^uGF59-#l%I82Cmumhl({_kyV z?3C#L&c@yo{eO&S8nQ7C73rsQJSFN6guX+bhYrXDC!s`~OsSB30VEWMp(de+s&1Lj{5uBf-Bo!t!F#f+f9fRuQehX!qnb#ybM{tQtmNI0>j1(FQ)G>bA!DLw#} zc&a)K@P|QwJ^-B4Bn;1q&xsTtR9)4IBK?*oW8Y^ zNJt!p`#caQ(ffGHaX`}1*+xwv6pop6gP(uZ* zm?{2X#@LnUL`*RYZ`I!eG{K=D<*J-0S|ROe?!jq}#tY=*&`;DDqbb5L#r}-=S0b6G ze20KObqt7;NcVAnhNEeU0R}E`CF#nKqHD@oG$)Zna)xD|mq1jg=|(hKSJN{|LUc1D zQMv?4MAd5J5b1d<)MgR7`d+~Lr>zW7_bM3^9uY}Iw`2Qpq!1YPA`SvG8!X{I5wK=K zK-Ch%44YjJC=b9tN{)5?0s8bg1*v(lQxvi(3duDI2Z*UoO%R(*)@d|h>j4=j(+*Na zj&zBxdf%;t!4erKQtiswz>+WMaX%o}eIWyuNDb=*N}fW&ZW-8|1w^Es`IALtT~A*J z!M6eGs+U>C7eaCxP1jNgPzTXTx)AdulvKU#Aow^)>z+j6LX#pIp%3cY2WX^&&{ZJb zK)b^r;6#XAXOENlm~hoWPJj&(>hFxlJ?){xX~ekN{R$;;BAM`UsCq+%-SHiiRv)0v z6!<$D(RngQQQC+gK8peY2(8I9V0ySfJ}0nsV-_GWV@VjOJvovG62x`_eWNc%CqZ!kpM9@VTSa4I3RJz77A}aq!`&jilUg; z4ul-?Eroo_f*i=01(mSgGOiX@;t-v(01cgK_E|Kc(}Zgd6}d~`9JCIF(Tmam(jJkV z{E<*j1a$S+cSLgP3n0XV|MsF%ma$vf6Lc#2b6v#~P-RWwo&x2EiO?*+b0!#am^;en z20`{u)x-#M&axXQ1q*7CYO7EYe{?IUaS&>WUo&pICNvccSoB;Xk;F0M z(w*4;_11d+_ohO3wZRO}pib)j)A#Qs4e6h-G^d37B$A5%LEmpQH(T4LI-Sg{>cQy0 z^+F@$AuY3h0aB!ERee-N=cN)Rlsf%sN-G@!vQ?UAKz!CT`D@9OlU;q zLY=lgfg_wJAz3yT4xglscM=(zDG)}I%;OMCvW^5p3zi@sM~IUN;Uw}2VlW}V*IZ*x zRfhypG&u6ZBp?79n_lUe1hOXSF!vIn7+`V|93<*wJNIWKNEEJ{Gey51eI$MYh?Qdb z4WZMS9H7mOjT9MG8wUOb;d2*Y>k{pSSH2Fw>EL&)w>sX+FEBM3TntJkE4@IsV%1jbt$k*>v%v>Q8=ZW=u#}v5fZU zvPi^=6#?c$a!i%r_aK{4(f@4ENrt8*QnOdbXc+EMp2^V;U+EC}tt` z7nZS`CsGR;H)O2D7DVT92y`VQOj61yWL_HBrvQ2YO{sQ`c&yh8$5b;D zcKH-bku4?k@l>6HEHWI)TPb5z4QY-d6$%xr`7vf@r^1<|X5I?pYGjL;zy}2|Z6Y!+ z255U@@B2-s8)9ic#KU!DtJ8e`Sa9}K@8!=PE$F2XQn}#-jw>973&gGo=QJRBZa#;f zW=PKkN{JM33=U*FY8X>3B+U^46S1%PoWoNGh|42OhLmt+sBI0X!fgVAgM`i1G$pW2 zh0n2St*a)YTUjKAx$f@neZOIZ9cXNLdWdiopf~Ri5sjvboFhS`+76gs!8*}Wd@7ih&{I1Y{W<;U*@Iy8N-3$6J>-J*ad zR8zy#!$#)AK-mwm5K3^S5fKKF4HXOt*Lsq4uR~wC%SKWM$}u29u>q9QN7zN?hg0qy zj@6vlnROPjS6W#m`ji?_QnxOu!<)&VfVEnn!&9R@0A_EzKz_)QK+%kA8mPUYhGhk9HJFg$ z0FB)?LZ{KgQ)kL6nzL-a+0-0IQ(|O4b;x7iSD33t+1$=K)IWpjAkAb%)u_sF=x(Y4 zebrNl6t*CRE_R+P_9FV+sW}SkfZXaR%pZfhcWBdy9cFL1kT{dfXJHBBXBC&BcQ2z#wipLHDwRnQvlGnlj#!7@D+6>LS_t;A14E}yRk91 zFXm*<_+o&zcK6;fk_h#A`13g$9=%c`>haMlMZHt7Ic?o>Ne?L*y1`W13y=v@({M$@(5`IEf*yK@ zuLu%~lLi&jcPNPiMOr2B^_pA2K~U%>FzJNPX#^8o=swlUO9!1f8HC)B{ zvqTuC6Vw@@nMExJgmy{|+4MPa6#AGd5)g`>&*eJ&aYYvBS4F0MtX^t57qJwGds)y^ z6-+ZdA8X(s&1temN$?x=QtcPwtHny;==-dEU zY{Yc6`{*=6J~7r9tM&!GFmRP9uh<|^SyExIRxA);BVtZ`5@?l(5&{%LC^BM>jUd%` z*3Duza0Ws)R5+sMv0MOmWOy(tG`(H{M#Lg- z8Jt9mC8j2tVg`J$Xv0CrdIbs@hgj8r18o=oh2+gR_Im*Y*C1gkW1~Y&$>D%iBmvl4eM8_-2HmM2bT?Ib zAQc(Eom-G7QO9j>>4qq??Fx+Qr2$1FSWXK`7jgwbM-Q#4xs*dP#3mChjzVPuvT?Vn zp4#48c(J8HZMS5<{0WTc({}mBwu|D4_kln*ZkEDEsXnJQ<9>rh3MqGB!!haLq$1e| z=axCwJGQg~Q&72`_?7R#{UI8|QCq{hw3>;Aoq9MiYi_r>T`Hrg1YD8mns8v*(ve@$ z_$T#lQItgr`Z>kOsslc!mwPUX_BolZ*Sr`O<;eCXW=oE$TG zsyVcBZGeh6iJ731@r996?9YL|3-J87ThlUG+l>@DmgedC~}(PuFwfmZAo zgCU7E4Fa37)NX@~e(= zgan%tGf@2kW#eXZ;-$Pbe9}_@uP(31!oG%SJ3o0hx;!84pN%d$owBN;g%)Lv;H;Jw4K z7@%L#Zygw^XKv=9gr}R-UyyE5n-ukl^!rt1IKjb56fTT-Z{FIAGV>GVz!6Z*9wIn2;`4%3}QO;NpKG0m68#WZCKi`hZ; zTF%J**EpmBcCNLHD9aObQ;6d@TqyYmXkA**!b`6=KnE~RXb7qmKM|76(<{OVNfujy z%AOrKZS71omYzuLCL;XInhmVjL8$B)9Xw1oElL}uJpp}9{-)nCHv_XuE!WSOkjF&c zFn$GwbgB{OU3TzWGiSB=wb`aQh{3+c>f4pb3Z?_jjZ6=n5ueTHBnk|FpxF$c&BsP* z1X~aqar+|~iaEJfeFuxcIMmuqG^TLhA*=!z)CKV6MM$p*+Q%XFnQCMB^En#*c)kXv z9JX(o`7jp_J3HIi>=8fVR4$Oi^>LKiZDow1HcC>6DrtnK zSdtsOaOH|Q5i@JRotk!A!vKN;b^`{Dnk@;kM26 z^o{P2DS8+U!!R`(&y1A)h^Dr}>HVbIrEmo7A0L7VwrZ12lq~UafoMc!HshU&+sPa> z4FmZzRXnh^spe;)&N+M7Io)Ei#i2zWfQsv z3pxj0(PXYn!qq-^?u)AISm6SxG;~Yi~@f*ItE~0PEh8$&YBas-nbG;q}j6Ac$n zAzqxTQz(fm44D(5eJ@l$UyzS6;S{c=L-4+Eeh;!vkH+yNcQmWYZdpWLoxV=zTYKSj zET_4|DW?_4+!7WRaZdP{(_l(k!?PloUOUv?=zZVYg@b;{y%k(rZD!Y2_EKtdW25rc zD*t`OqcEb?=bw8WwbEG!0Pk?XmCxNwa{=$)3&Ityr7zxamc)R40hT!LPm(_-Wx^xH za_los4vx>!9|_?LMct-aXBR}`NdL%ME$;aXYY%kLTEGuV0{+3BR13B?y0|a(fI1@& zn+B@R(3YcBWh=((fKW8=?!9&lmhwwYrD2*gvn9Yx&O~bQRJ!;poBuEI16seeBhUq% z6UVWHg#lbCk;vLU32Af$*q8Jg?$H8{X=IR8{i$FK(DsIC;UHrsp_PM7uf{{>$TA7q z$Hh$Ri_B`=NcD<$_)9Sfe*y2XR%RXwl8Ae+pZZnU+W_E5O=B(T=5C2>?(I}kO-p4< z<##u?p6>pgyZ@byC(oZJ&!2D8^GCF@J3Mb;#f+Lv01s0V^>teQwKBpxOwEi}x4m<0 zT+3!}ipXx()fD$(q_j}PQ9DT@(h94A3t{Cr~M9gXtIW;1_M=`kovn(qA z9h}PwlW>n-WdQBTv+T*U>?`vuGZI&J{OIT5T*uximfYQEU)FQ1ot7h;pcORzD6eqX z-Fx|cN#_7BZ=)v3dUOwW0S{#uQ9c_U!3LZxm+D8i6zW%(>0O)o3@?j<{e#H$OU3%p zeM|M^jzayDfcPXJep3SCqidAfh@)=oE^Xejsx&$(3Zt2`EBYoR(b4S%(a{n)bn8hD z&E(M0{fnWaCn?mOgQwUg2t7%l|Nasvc{~C1F1zash@RwYlRU|j;8`trDn#~WWpG$^ zqL|OiZ^2E$rrP&p*01(eV~^o;mBmbEFO!jagIHr%*3yE82Yq7_+>ch==(jSkDYkbH`JZMDgvjv5QdIG3swMzYLc)~exmI65yjLY4j7i9D%qgN( zAu*FAk`(spm=k=ZtJ^;KY#xV1l5nAI;)RZ=i4}*E2rYpGOo0{*G&19Ovajn7T;XWd z1vKq@3ZCVuDyOyHM(IuqT57*}aqE}YXex@PcHg1>oRz5E4tX0kwGwkH`ACi<5o4}+ zT`;IM)9psxu{t`aX&qclkk`~2SDQi5Sf;11F`4aKS&~#XR?p}k=lw0L#|;kCL*9D( zsVaYSvf=#rL0bng#c6d4h2OMoWdw6_)F+AQMUAhwVDV8(i1oGcb`th&B@;h)P5!+M869bAJdh z4kutT+wE4)k=oHTB!RY&OgrMN&zM+*AOUb|V}*c8WJGvVLr0o1vmxhkU-O@OCM`A^ zXId&15(*TODfSoOb+0fw3nQ|K{26CarZNghDDjVh$VUdx@LOvoBMj=>AJbz82^tU| zbJ5e>XsVRN5-EmUn!sLZ`RmQz*1r@Vr0eaoG($!fjr)`dsd%;Lnbnc%eHP=oGPreY zY7H_2CjA1GAnV|EcEh+EyEt{Z1;$xeCt53r(g<~PF&vCLL07v$SwG|vI6NuVrTgRi zcf-r`i<7g_<>|@!#hbIy`Q_>G{QT#WvxBZ?+D9L;qVT$x*iM$JyVnlQ7gfN{8aM0o}htqvM~dM{#jrVkF!hp&!Cm%38!OynkMKrwEI zIvK$`TN6At^hoRa;2vHI0Rq(>XKJ;v{x3B*CTcs)NI3Tb=8H}oCR3`l3I(;Wl~|3m z$hdF>wK)+p@=+2VsmTH9qWHP=8tnC{1+R)|9xG(iVqKqdKcNzMBvNV^Zl-jkOU=FJ zzRm~^KNRG=3A~~K+sGoT%*#ciWKgrYcun*+_v8o}_H@EO! z9E|TT9njMJZ(Ey%{6D+fo4Zf>e;(uc^hxoq&w4-U6i{aUdlk15JsUgn^XHDP)!WB0 z9@CIYNC0Pt|3QVDC&$T%VqlU8X5xxQ!2q3>r>iq35(ik~L7MlhDdu%%{^B^> z6&2Vzwb19!9doxJDqtEw+89Ft{|iMl3KT1_qpFIS_^M0AD;?EV3na)_(|r*+-4iH| zjV*|g$v=Ja3ca*ds=B$Sp&|%%7Pp(WY(c*ChWf^y8u~wr@#E3|-JPux{oi_`|Bv#( z)|`~KzGK0%GTqNxzmzC-@w-c_>&tC+OGGy_g*s({HH0GkYbU(W%L}mBIU|!a+W6GIZRgidhot?6Js5zUfWtFqCy7FyNC9{r#V?(lE@H}j8 z+L*W%<{P?T5o@_|*OKuySGVnX7DJsszw{1oehT#8kEj=5GG~z>_m&a5nf`BX?Y`VC z(*M1u{Lhc`6zIPnQJ3Bu`U41YPGqNwPOV066@o=Nf=GehIz5Q;B)61RLaVDh(Bps^ zi5G%yAJRrAk%xsBvB=|a5!JoOT{9Vtx>oKwoRRZ_+>rCA%$@+JRVATef-#rL$1WxG(AW_YUt~&IN+f zyqM;OD~BsDVooE8p7kzF4|}h$fQt>(1;V83%yA5ask;U(iy7Y9-4n??YgzXjLieL* zS7fn{o@qV&;D;QV<;?^0^`RpA{26*^s{e)lNSGwWh76qty{f?mgsdITI>I4~rrE8) zKBsVlGz1e4ZG>;?BOC-Ci`Ef|gf=>*5>0g`R9!jd zl);s&NDbT|lp3gtsT7WvIfiVjVDKHJ_31=`B-exkTtu#^FSD?e7JQa<#N775{`PD? z9ZZ5b$nA5-a3P!3vN;1Rh~)&Tv8}KWH4Qp;4OvOo*x}aARRB?QnE9aFm^UyxdP2>* zXgfa$1I3IPqkcR@CKKYz0Xk;JNYOQq%M8>;AE}Tc*DSwkL>qhUmtuz7DwxET@w83T zZkM$IWs4r?ZJ&71ZI?*?osVwxM7aTJh{s5ScEND_#XmEJ!} zh608#>OwGq6uhm_%@Wk-ZEFJN3q{O)5=+s)Vf-p&I1v4q>Hqb=bAkzYY~oonJwTe~ zI0L)s{8U;xRItZ}sufHl&|gS$I0ci z=xEewRoCL8N5=6aWkm9IwP9BoK_-mPv5wi&MK1#COVp+MSE+ZXt9XMhdbU)Aus_JJgeSTD_BE$im{tKh-= zDqyA2WdT)k$YbK26!EMIB^!})j<3r0+Ny_Fgv8#IWw|}v&tTHEtmic}57pDXI>WFs z_1oO4q(;jLQDF40+ zD%hfAB`b(RCgLom0@o}|=H#7XK?{x@ zUM?K;j0xFiv2-Gjx{vYCjHoxJkrzb2(0J+B8D;LmkIE(SrIJCZ^I&v(yK$K>#gjX2? z%Xx7k+)id+HOk}!zV{}CJw19 zugGGB2CvA%ZSToC=c|1R^#5{3!kBQ;lkxr7{`|*M`oF!iwNbSH+}YjPe4_u4@qGH! z|0lYp^8q*rPiROK$@)Q20qoDn0R6KM8@T_^&gf&TuaEMXdVC1Z-!bJplf^zqV7=@U z1dcKR{ef~@nxaac*xbfSSW6-w?QI*m?L3)G=ttD`x&`YB-8Jj+p8CXF)6KMl>MM<8 zny|&Xj@o^#u%Z-u{|kc0VV|Jx|EG((m)+GURfvYCnl@G-)DKTyvDRGcL3jECY&^lZdAUynaPaY9hZ~&IpbFIO=b$i3o9d-;r3=mgt(H6UWm;yuXo>pcFEME`cCz}=N$SPbJaBT?al5O! z%D5Gw5v^!rg@NqtDNpSglj~fJIi5qHFl#Q8hO_!V>rQN{btLih|Dez7NLxlfL!UdG z$b?5?fV4;oDOABiN^2c4zPddZB7&);xcu zLJrimYi|O6Y3njucr9yPjnjpeS_J*&XgJ>`LZ)F|i=n)_4m2Yx1Hk;6q*8P*WkQ~% zqH;xfM~rrAkdHC(B?+uVQLg8n`Y;h|8G`{v@%pm3evWK(u@@0(F15VnH4TXT(PBx8 z&`OGn-r8En&NF!CzUZKoAq2&V7TrL1V!Uz%`bXxE`;R}+IFaZdqBIaHY@EA){LzDS zmKBBID`FfH>uY@in58Sh(Zn-B{}BIlYCx1g?9UL50(wn@1Y*t1V-}G}_PQOE;Y(>@ z9o%SM1h-&4kj`388B=7+FYr|FfySn?9kzVsvnHg4t@c+QjMMyDg*a_3_Oe~CcPoG_ z4yQtI1>vto(RH_VVUsksRRu7kIW+-DM*1$>p=u!&KnCF7B0HiEDffUfAyaxbo{6g~ zdUjm^`UApotU9KDzHo)2XIIq=o&Ak|XEZ|H^{%PDbf9L9(s=}`Og5Z0_C_0m z+l-EXIy^f$em6S4=w!A$j&ppzxQ>N$@yqF`ed!BnUyR;?mD>5~ zaNhw^?VstK^f|z1?~gAI-{nB>pB!JDog7`B9u1F2m*=CipAPp&)*dcvq#zE~>S@X| z^=5QDIvXBA{Ga{N*~R7S!y^Z_wSefGs7`&&HNxef=Mygb=1#C4>W`yeZUyv;EIJjI zr{x>pKUqY&rGdSBe{^y9-zUeTOZeZLqmx&|qs!sJ!P)5iJikI!pwpYep@}w^XJ~Rw z9h}0-jLt6K9-Lo}j)$*~Mh6ZiLGYyl5)DreZKbo(@Zj>~_~;j>isVVLMg?{Z58fS~ zpC6taTTrJbXBW%IR*{X}p^@F)ot^FODo9%D1|X8QENr{vOB*>n{;$#g#mO010hZ>t zU>syEeO4Gm3jh4@V6;CxgRM6_JRY509=;pCY1(fsEc(;@woP{Zxh|&tIXphRc(Bgc z?6K7yDqJ39^|ExkD^2aI_lHLphsT!($LE(vCvV;y9=|E!eBCB%M2X?;XqO3Hmfy^y z*TeTm7w4DfKOUZ5z8jstz1%+=4KEH)j+gH&n+?3DShjaW%-|+KYvQYy*@Syr0v8Bf z{letRDss4t8kP=_dPw!!hPn~7>`+NG!=o~fa^7@~7RgdR zcEYlWIzBlVU8+_ttTb{oI;-Gv+p(rg+DjTYvu znU~S4)7Q72&O8(++_e&cFMk@I)gSfc-dDxYUVSL9PG7%1zdRjYylvTR8eV(#w)Wqg zoxDF?rLim~_v)=(>|YKqE{6MWFArbWp-;KRT$JtA8$CTcd2=>=cYb+(^8Rdpw8~h^ zk+-Yyxz^Fo2GwpRM(Pka7#&><>qXxcc%;I~bw%T!Ae?fWqez|1z_$N1Iy-z_mZ$3u z80}Pcd3thmxPLgR5aG+(r)p(fuPu{q)WvVGBsbumtm*}49vwtf$Cb$4KI-<>;qQZN4dsTXhQF8TM+{xxw3PF9#QX-$~~1r%swHsg%v$Y{A}mbGTB zT}jAkv*sRR^H?sb>*aWT%}rdB}dPtEmgT`ILMc) z*MMjhO8_XAm%eLPJxz4fj9?Y5DAc-BBh56UafO!7!0`56t3BQ-dzQ;y)TV2t)J02n zN~wXRo;_dM0rGGD6#Re51Ehahp8sWYrx^cvbNea&!=pTf{4eAw{mU0g|3dC52?4ot z@|Ok-_{ws>kf+oycYg}>UmfNi1idAmn|jy#o!$OKSVI3_?!4S8#{b#beTx6^D37Ze zT6+JwhF(BHS9RjtdavcbMC>gI=WiLi5^ME(CnrsD&Ng^$4W&sno|Pj+7hRwYu0rT> zi?H=xsP!?4+;*yIcvWZ87(>fRcPT7>*LtDO7`y5k{kGYI-Es-LJN1dD%8$u}apJ*8 z->93{;g?8!pOAnA`lmVvN#NugkJv|kk z8?h-Do~T+>@H{WKYs$Suja#&;mDa60aW_r})m!0il&{)>&Pk;Za(_l-5~g0)?*Tj3hSsTxI)N<+?w z+O>!Zvn>OJCz5dZaQ14r?_@Rs|J5?G-Vi#SN!8Ns!{D-&w3@T+++lrCAz4qW`A_pc zxQS|N(r+|iYDGYQHF)v330-uP50}z~6D1l*o8{spdW~tAaN-KKlR|IQ0`2P+t4hR? z!mdr}Q4C_;3T#t;kJ7N35`MViUxsGrZdo^4yTxR!EuGuiVE^rt2rQrJyJZw;7q=YV z|B(<;3_7k>DozHdyR|Xz7Qf8Nobd&Gd`AmkP3HJ&U^`8~b_&2MGe3CfxmtK%hR=)d zlgLE9E`3q6T6*cFv<(o(v(CZAf{t>k+Tpwu#k&Hu_3>{2UV*1>7=fZ-@}(vB`*|wz zKLYD?f7<`0`k$?>t)l*aXLs*O|MMtMvk$|ql_X1YcSUVeKi0=wZpg{_H&ax=j5aXh zO;dG`(*Bg(i?FZ3pj+h~NL3D~B*EX?>fBN`ztbW;l7Ji0mBrbmIr>`f^nbgP_1k#L z`5*2z^*=nz?*I2TiuwO{wsyCk&i}`Fnv4kD{O7CQ{#RsnuTSp3)pc*|>&tC+OIPo2 z75P(gT6oHWp(#xGs^mErY4Ogt@C^we(tOdXn-`E&K22Iym0erCH(9vTsD^DxaA8nR ze*cCTCT?Q=tb!h}ljOf8gsei&X=Y{I0o56&zRAyB%=A^;zOGc(K<1KzH0SnjH_~Pp zP0Zq#-T%e=e@;A;FV{HdFj?}`3D#$)I|-0Np9b+GXJJP;?* zdyNh00Z_I8xa&QCdeM)b%^15vgC9`W!NYa+x9L)M6`KGTf4@;vfOJl5TEx0r3~%$R zx{^yJaGym!md%(|63dr~?SG*pqJR8*NbfF^{#&C&rKX{y1@$hDDfuW#q?rIcyWKj? zfu9J;=Jo^l7*icvxUmNaT?Y z{7on5SL6u<&NY2i9sUPgF8b|%gQJw82E?B+)U7NyVM!E#`6Lx!M-HA_>j(z{IP@#t z!$46?k1sH_y0jmP!;VMhu1Y3JfJU(H(=HIEiU>rzTX1 zqWGE=r_Y@z_34N1vlr&tFHLO=5IrwqCUf>$*PYl1b)S7wMLu^cXH)lF!%$sNvn5&O zoOE*)Q>g#n`U#xS2e6bWQ06adAhTqv-fk=2o6$;9bbPW``SSKEwI(Meb&BrjigqTA zW+jeRq0+3x4bNf7WciZ6-%!Z8r61H5lXzOlFW+b&lFrMEIpI^ttaihxB-YG@C(%&6 z7le1w-tO-9&RSst>&!>imfMfq(o|$~qtRAmcW*UgkrlyNa}o5bIj;!F*WqOwtgOVzmPl8Ra^m{*uA{E!=e8K;Q&HZOF4vb9*Q2gL+;WF} zE5Zya`hN=Gi+*r+ojb7j};0d%Fc*cK`cw<7LtR zduMn1N&o*C59H2D=6K&+S)#!Y4VRg9%JSRlGjdmy2J>!n{hA*_jix|@2dqxD1Sky4pCQ2G<%NNM5l1}{P=?gVE_pN$%ZF=^Hm*~limVU1RP~v(Cl`094Zj?Zq9t$Qho5R5m6*HSpZv%PO(zSK-+>|p7rp1I4groqW z2hl^sT~uR5h*8a*OL@Gv!Wm~W6oM#d>-tY%eH+8uW0}Ka(nNI{y)ldiyQ?jbfDr!7Ci% z$S3^p)bv01Zq?pYLCj6h-8!j*cb}=H7Uk}`UkjJY_tl*CKIv7DEByh<5A8jv`myRX znjp6Z32_{saweJ2!j%D;dU*}xGTPCAY29NMkWwetGf(_2hU8B$57s)nZ-%qdX7b;qz#=w51!C zssxQ@5X0h;TzT?W+_)4s-xqf-vKD(Q8`?d-XHo$ZZnMVqWuIS zrO~h#5qZP-6^*8utyE?6rh*@0A)I|-Uc0k8A)G`$nR4vT_Dsr{vqV<=|2_I--*NnX zA+b!#(@=b$joSUvu!1!ssvLvQ7!PPvuym{JJsg|2L!Zh8Dok};9b{=mvP-Vh{n^eC zb4B>(P+vL?1?;=?tlXMXJ_MaK(V%MyC6*YDTv2{SBSEJ#XHU)B{`lw<(lnPoIm8X# z(m7#C?zoYhIt6^n{e;RfC-{o+?EN+6GQlBB6(XV06wNP= z&JkqTubb{f&PXJw--fw^COEmK9n#x7EbdC?viz!KI)Ndc=-?rzE?8aBsr67t#qq#6*!bWdu59fj~4ug3XC$I_DV^ zc8k-pYKW#kx5V1}AuSJxytgAj-zMc*<*KM6{@cA904}rt+uqnL+W+lsZam%pJjUbP z;a0J}FKV+S=JNui@mMnRt-{UH%JH&l%>nN@TAc0Fcj#h9(EjnEW!DRqf07{_1nM6- zBS?#q$e&>vAr>Km{TVQ>y$(WtL@y1HOATaz{_qmBax&zeR=xz5lyX*EN z%)~%<9@6rsU*saL`4Y0}Wk{|`*v!XjN}mo?o1`c=2@iG`44 z-bL0nT?^YKGnp^FfxhvlK>v*j&&w`b?%@zva{s^ia(kmd|My-#<^Ot|r=a$z$fjNu zm8ukxYE?J(drEg@043d+$EeAZuAI}p$B?GjXy~!oX+Q_s- z?x|>}tR`9-N-rsMrRqooR3N4o5QQgk7wSOqbb4mEz++n}r*e9oiQ&>J01=Uoqu|_+e^85;- zdrMK!#(y@o>$1M~)luZZ={!!0-NgsjPTcLia7lDs^sm+vd3thic|3eK%6&A6eqVDo zFZt7=2_<2WMy;&)rkz~vVFCNDvFY>E;r;`+58Om$Cm{9l{>kyh*~!u6>Cy0bbou`5 zsH!&u)SXEgi$TBN?4D5aN9fftH-p{1m*02W_G^EbOUPc)!P?Bxe(d?!W{a+E?qGN^ z)J+|n4G)hmFAl5mqHYTWs^TKASZQ|_xv4Vejaq{En*k-_PHzq5%s*#z=#+n58 z;U=X`^QUJg|L>Q}i(gJ_M%Xpua#sgjsSCh)Qf$uiLN@h47XP7Qo>F1_uhhq|OQQ+v z=I70^{LetA`(N9;Zz}o8DH76a5)mOzIUAFFU4^-Ch%D}z7`95cuMYb~(LY}M8~p2E zxi%2w(@i+Jm~kRzEDQ!{b1PQ>E(>vZKtiyR)W^WyMy^bpKO}Z@A0Z}<5N->(@5VAC zt-bAhRU@Ha>m+PG)*M*9e?_93O!Tl;5KW0}s+oz9PKdwoLy{|Mx6?-(6|7sx$_Q$hkdHCdEOEBA_BJ*u zN@}uEwe((HcntmMv4fUVF`8b*v8*2|$-ErU#58e576VjUHS1ty5f26ICc42q7@i(h zwb0mf69=ZBg7&|zSbKJ@II65v#l~u}sr2T_4?w@Gksfm#o(x5AN+QB>*z@suS}%1! zT{4l)t5>;h)!X&1u}^CBzxV0{w&ebAfc&j$iK61 z*lGlzu>ZA1_C4o-S$6-wyIsuxvb(wSl>g;Xo??=%#=DGv!^|(_o=6dCc@g#SX3!C;LWb7)5QTKYYi@NEjF6wqG4vw{)lTpNE8D{QkZGvf7n769nKvkW~ zLioKXep>6L^0hw0^1UMH#gvqLW&n2U=C-?rU~ao^7wZO4B$J^aRdIZP?3`E6P`kuG zhHydT@ozNokDK$TT;eN8?DMxst?MGv-(Yd?Db|K90rQfTYM8PX{(4`+^Xc!l{Ey?L zm7CQsoRH`4NkQyrzIZ~A5>|cs)c+?MMIzyl2nPMm#){F>994kuL=p}k&Rz}ovu%z3 z**6nfm;7hYarh|){ich&m{=T_KtVH{id*t6e_ekM_`?*m z3SRtDP0uTB0|Aid$wgd(@*LK(^w#sLd-Dl(E%oaT&?nS2R0{sCmBdm)>Dt_fw$%CJ2xRG4dYE z|9f+@bpN~ebpQJ}Pn#It^?Lp#DFgmw5z)5+R>#{+-fJT|_OCblChF?G!pv@!FyB<$ zkZ2c%OPj4h-W*%XXhstfu7y0?H=St>tS!?L-Bh1_$1%dOb+>`)lkX8m`;FdrK0W!T z%>Q2nLPxsyLrNm)$xwLOF!HwN!E*b*g8%o{#_r3VC;I;wk7nXqq<=BAyt0H0MeiyQ z|IH$@4@lE9_qO$SYzm(onpE^NSOPvX)XlEl%pH?KUU!-E+N(`s{9L1Ze<}zFaloTkdDOOm3o-yR9N)*zVr<8@c<_fZ}Py zgrvU6)N5H{u$h!`5x`~+fGXkPm;$vRyb(ydL}6m3)L`ja#Pl3`&{NIbDmPebP=%#( z=4PujH=@Plh)13sagWne1*-y;wQ1pUrBV{nYr+K%yT#ev+1aj|+`xqJ&rh*7)@~k8 zT2+q*hTu5j@9ykucXL|jszqCEsJZ-^W%PNummA?62ci|`I1s+i@9wx&{f1yux5#I= zidui?cCyOTbN^47|GOb$up#hp6d1+N9d81f`M<60a{RxSyW20H_`k<^9J6$F_`Cpr z;(GN%=JWh2iGq-oj5^k@vO!FJV~}jYvhLWnZQI^s+uCE>wr$(CZQHiZJ$`e}eK+F%=*a5T z*_l}@8d;fNeO0#oV|ohf6$hfs?j+$K6GU$Q2h{g@`4SK?E{ccv!1RUh(IIddR!Dl^{h=33f?+%t1? zbJr06)IVJnFs+A;0sJE?Ok=syer)9Hmfgj!uCuOeJkC6qHjS`dLUGwUEKJ zx53dr#(4H1;j`4i_2FAfXKKdk)Bog!7S8u*I+N z^V6(K$5=`9yU45G4_v{JJ#8F{)3&$QHbEzlAAy0os0fm?OsMV^3bpzMEjU%E9=F+NPlkJc4&XNy08%bI%DASZdAvk zK{eiaCWAY~+<973w1_3W{1+r&{D@3-tnr7l_e)4{Q-GX*^ElwQZL>d2@p|!f?*CD- zfYBM(jJRz4)ai`YQ{_NNi&r5Xor*e}aIoClE)&uNejb+xh$%z1=Mxm8A)KW%V?#91 z&4PHd$oBKz>i&0Ja`Ve_^9Ip&01YYG4CqzFWSr_fni`n zqgztrV#dU*v}v9DHy!s}?&1maC$Ss6333(>LAQDkyVyX1O^fQEMUS65u`>r;pfAa1RMMeqi5UmjYcXucPinMn z%4HNwm4Wl~XEp~iDg56)ML88wFW18bY#qsBiAg;``QqN6iKyu0uuK5omq!vI7R&j? ziprwR_&R0boi)G%_yF`Up2sWTH%qGzUt{ESyQ#OOF173W!hty`>94y~o= zb~24I?{`5|f!$K*rG_ku)`vhUSrOxjW0kLL3*4-TYU3Um)-?<0U-WaWU0IN~kDCP< z)oV?CcEttfPUApx1)z4;V@$Xbd{pF@(&Nmt+PEU;%0FP0nTSg$Wvzzd&DjPE*L(DF z#qX^Nr6bC*u|K@VKe1CuANB|Q*H$(uk3Uz}yl+4J6TrgU2)27D%XIeWVyCB^oeq|E zJ4W-m`O2D59sTPC^0g>;#L!v`SV52y4?s)|EYFs-r+u3|e#T_4YW{amMHs0(av?;S zxk;4uzRuE_8d0&_RU^46sFId5X-=pX9?IH*?=h_!{z6Wvq>r}S z`6-TFWMnhIcf;wCOO5Sm9dEP~sOBIJbQw&XZr||tTaX6k#va?j&da;bsWLUUGBbam z-{Ue%m*?r7nJQh;l~A{WOOaWU&qTxvN*=U2H#zZ?Cy|N+xK?J;^1|{t1Jlkp0w%s; z9IvDXf)huE!U>X|!NB^)lf1k_i~0X-|LoSTeK-z&tlHB1JWuKUG}W$r&qn^d2~loVTJ%-0^el}leXo91SLh=%CT&Z5+~l)v8k4FoqGkQ-O#|TuTanq5yvpj zA3j#n|4ul-oW)&XM+i;aEM4VD9-;mqc5;!CPe>HKE`7t86)5pttDqUER4p}c$m33W zXo#ok)dJMWz{p2rwg$kpHK0@9+)rHb6k~Y3eQ~DGg-MR8kM_(%1|J}Fmq~gyhUBzs zuQVugy91^T(-zXFS#+{pBQtDEL6rdM@+gy)U?M-L>#42E$}Bp29O zvZ;kj;=M>o2Gp_zc8mfE7a@c!w@++ew9n0yjE1;RrNkdDVlU`Cb(WChi+^=6CSq@QVd;NsPNJr% zAEl`ugOU65Q5DUBC4&d1jV>C9f@U>SS@M&v#Cc$o+|B z{(5Dbc!ZgFwmm4(k_C(znlN!iT$P{2uF;D`or&2h;H86G^z;coO4cAKOEZm@4 ze>YX&y&cWNt51+#kvG)&TOoC}=AK!Q?7_O5u!THih=_(LO$`(DV31Ksk~!HQsTmWQ zeEt|Au17bwzMN6xRTV>=jb-UR5x(aEv|jbI4-m4OoIsuj{e$Kw8AfHRUA#g)UU8M2 zuA0cI#agRLn6+LypWIMh_ZO7~ot+OBVP zU7l**^&2Tov7<~etZs~q!et(+>)rIR@^h*bWXGOdjk=qKbLGQA3&q7CSO2Oq+NY4+|e9>`EAAi={6MK6@7Ix{K6IUxB-s_Uzr-R zyV$8`KwOix)8IA}Jzv74+j{iYy0?nJwI!AF7Q8;irOc)2kv3?; z1;}`kuGOrzVDL5Xr0TEMEySzt@OrZFy78d~SpND)LAQQKfcyrS2$mp=zNoGdNbEH5 z1o+vaQdx@aA?1*7IOjbO5|V`CX+fu$zXsCJ_3_G!4deIbHF zXGR`nA_a6!EQ|>eS}`d1ZoXQ$@KJ`a_)J|8fIS%-b0z^~xV8){m2}^<^YRnOqWu4$ zwVj|rqt2Y_n7w4=(e$;P5kB}32U;TSn$nnMl|Rs#2Y#_W0XAWnO)?f%qQ=|=X;cS? zV?OfP$HHQ^^1h~oX;vvK1-Z%C-h221jxE=>R$JAup$#NX+2(m{q$cI)c0^5&`?PPy zE~X?|uR~R}Cq>)>+g1qM4uIS|-6a+OFvvC+&Cxgj=PtQ|?G8SIKuG5Jm!T5#GT#u@ zLI~&u)&d&G}VAXlP7Y_6)CH?TAdH7Wpd| zHLpU$r^;|La!$b7h1b)%)?eA?rL-mJrF3AndTmGkc?a$=sH8kRDiE$D|Q&I zA%s?gVsYI(RBywuYRypQ%-&yz5zvqdJzFpq7=m$wCNeM4kjaq7_>JTn6TVMKBe*&? zV;;~HjG~J9C+GHi*V;V3Xyw%LZ34A(`JR8mMdQWW_|$z(4V{lJ z`wRgGkR>?008~ks#NB}d<}#BRZs zs_gt$7KA-(FFo(tS7i9I8k-^S^|M1|i4wtRvs+qp|8*2nckHset0k8;c}IDwtj9U_* zQ=z`{K;iVFGoH(+BY_SRx$@;fAKmB~+n zX6_9_LoScV)ekmL@Z-a3s5--*bb{uuH^q+HE1r1BDX z7h!}YQu8v|B0Ks(t@Y3Ts{NBJ*UJBS%?2`uJ?A!KBFDDsB zBITJVvXkkLUur25fi@H&TY5U#`MW1OTt*$?{c`$0O}H#)p7$xX}|5I95^@Sb{4_Km^ zbxHn2YZRZe7oQ8Ve`Txk4>2{sZz@tznxk&!k}y)mC|6ipzRr)tM76+qk(G9yYZ)zY za>F_T;^S7cL`)6 zfpr{EX~BYqP#O43pZ#U-HAe6G9zLR!4-GEg~Yn zr8(B4M&SIGly`0-_UJL^>U1*#3_cL{Wiv7jC@L-Lt67S6vh#p)ds;n)?o--~cOHbX zMV#fAs$)b^SJ})ME9H^_9pR*@(IEQUc7H2Y z+TDkKh3cQ)T^zsZCtpWLPutF4K$LgF5B{k}#gDHa2|>BZ{om!|l$9MVxF{%kV`ja| z;%|mztOOsgek<(rgDs-zkZ9e!zrQWr2r9YI0qbB{38l0=p^Wf@EPi>gh^|gqsUXYL zn1Pp1dnARlm7Z@DlywJwqla*m`gtl1@+?@NNH64)m%mA$Q8^hCv33bj*T|Er zGTvBAUnpS^l?^gf)`nEGn)lRBB)!F6nnNno`u0i=s&15k*D zn;@|>SAb882$yY)adv8ANOV|}=7+()UzWu95XJ@7vh*fdVVKZ?fE!bxaUmaoICyag zrr}2~5BH`AULCpHTVi!OXam-SuiY3d?%bxH>iAY~k#YkkhSUDdy-x631w3&gmNndr z4}|fVpb55Q9`^}#%Aj(022CSu`%8WcY_0_r$H~A3+unkB+yN>d7^>HS>dT__nrNdH z&IFYqX^PfD8q1>%nt0O{?dSBEvlS?F75%oof90xi-pW;svY=qCj)mE3b&orm!$jf| z4SJd;t}8tRemvQ*@Vc;{PXuizj(B=XEqk9d9DZ8=yTNV8|#c9ioA2kF-$=)g6N{GDTRC#CkWC%6%{YKCb5{6 zHzQQu*XxcOF^d)^vJp|Lxe(zBbbJ(DF9C+wu!tsrm;g9+6wc(TWl6csslKUpXCG;e z2^Ja~J9sZ;G>_xGDtE5RVLH$-*sa7RRr&n`Ey7>*3$? z$Ubrrzrs-X%1Gxz7y@Na2SecvLc19|*A$|Apoe2C)F z``~RG!oP*^lSwq27$4o|&b>l0R=3~2VjZaQ{`5kl<@e6r`sSVTa-jHrRi|=4&Fh_r z?up#eO9Xk%Uw%rGg-`5LLR%pY z@E1kG)w2cw*dviB_!0X8egH*ifXAnS0!7yo0A6J;zEZ@qu;^}F{#GZyFx{FIWQD<7 zPL^MV?&Y%H*=|w`>DaB$rcHgh<7iNp;ZKzYk&pI>5M`}nN$uiZn*FdNW(r59X{G|UGX&IfyD z%aU{Bf{#_;R!6Pd$IO4K(aVkyKJ(Im#21jac)G>>7$No`^5#x|nDgFxRHBP5%8KqX z3#?r7==YofU+Bby$E7lk_7w3UdgII1OLZ2~1zw?Cx>iv7!n%zcSZE=B-DY9oMWP_g zmv4z+w*99)W+iG`-mPh&pdLVO^Et`a_fv`5?B)fD1%mZe(_ZvZ2dsVaQ{UaG)bz9F z`DXOd&vJ;>6Pv3C zb~&U3A|h-+i3_e0ZryMX+X+dD1kHGoy`*9G`NP*$okF2!0w%)zdVDQge0Gs>7^xTG-SWo z*InuL$-*D%*@w+LMmijEF+^L&Chk|th ziNl^bXYE?7Ys8}>korwp1IJC{8&7zd_7kAaIY)d{Bww9@w_qvW$#<0G0W}>=Ls_2y zQFP|xUT0Trw0BcvRh+iH#JZ)x0BVZZK55+DZd}O*vIwIC&j{Lc_QEDtCy+gSCl_bSS`oBSTyv`e6EWIW^a|h)9!?5Bo z1=&ONS;M1G{L|Xr-Tl^Z_KUxD^*jy#@r*j9KKZFYxYu`D@{VlC&m-1H#bE!E@XPdELm6E_SZ&gHE&X8pz9v$%~tO5GCzlVHYBL z5#Cjo>lb~tlm#b4fNNP#GOAfk=3b&nE^;_=~`x-Q5G35 z^a6mYL;{HZxxj1W=)O-bd&J*v@dKuL$&{Hce@g;^KKnww>ymUZJf-AZGeuk_V>K;0RZLhrmNj3;43^+GUYvhpCVKHvc~q_YN%doMJJz9~ z;@q>S(U0oEHW9G2vnZMo3iAmlNR2GX`zTcsQ1J?ld$!mOUhPJdX`P$~5&|Pm}M-1X+8Ocr&-irF{0onx4$;PL(94 z_#MzJRINtPveWW=#CE``N=;^<`@MUXYNFk!C%ULzKqya+C$73Iv819|h*WkbrK{yl z(Oy!|V#V%d4VtoH;x8G$O>I(+6wY`sF-S;qbLsxlBL_hOEp0N6G6yYmX>Fnm2*~bs z>H|&KmtK`s-pjz>e1|D^St=S0TacU!yG0tuRt|!t>z*2&g199-mJQPR8 zH^(4OiGXD|qAk0}WxPSUF&VIMp(+90G8*JRQbu|DfE#Q#J3V%yUV8sXSx7;!` zrJ6VtfRvBHm1Bx@P_@v_Q^+HhX?6BF7S&cPSnR7O@mz~Q!Z-_Xib;^*v*KQBd1NT!nL)Jjv0P_;n5LEQ~M}uq3Cmhd{`0sFh+x1;;^; z+qLV${6hC6CF~d~p1_0~Azpg}RmPYTVk&xqxU%H#WrltK%%?SEKfJVH6~5iXXqDDM z5GDEZm1xoIYxB!ZZ-R{|v^iN2+_x;t{dIqro-F`#W_uP@1uM_3G>AVdf9RL{!%)lBbYZO+M$$XT%B1ID<_w^^Pzm1pHicEZN?IJL_ zp2n}F{2?+0MjyEWNy0&oVPXp-@^*D%n&p}`V#Tr|(>BNe;IHHrIszPx&*Fh0#}^x8MhZv zEVL<(m=1Dzg0Y2b6o~RH@O0C9rA#%}V|k8U>hgG|zH8~$5i{&dK~2a;ac|}!iEDF> z7sd8vp?E{jK@jQ78C}g6a26%7W&a6UZ}5BdfRc+K-p<%Q=m7YrRMyG?g(X%pA>mQ! zF=Z1TBXQQE(T$rfHp;rWuzH_r_D%q8M|9$5|F9hy2RkmF5*65c%aBJGnx`&KR*Xg+ z=75r?AaQqwYmtZyw*@?qF zh$+UM?kFja2P9jC1T7S!YQ{l9)3vBcmMeA~j*Fj9gc4DT!6K3+iv+^1`$g2R^d#=B zKIrl~aG-u!-0})|x&kVWL^Hp9eI_jF#H{}fshFm9j<%5X-$um69b1|E^n}_O4Xfny z8?}AM;*gn%E1tZ@0T4W_O3w-n#KV%A@p7Fr2^-bztW4tiV@#(gbM~GRp4~lLE&0W| z0%)7sF=(rj4kZx)GKiN5wFquLR$bTJnV{gK-;Te>XFln0X8!3os8)UwP5yvglaT)t zyi$pRyu8X4avx_M3Z+_Yp2nXhd?^2}ozy3dr9j}>>=P;3foIGJJv&K_9Orzi^agWb z1I&Lku^|7pT6JMNOK*NTbIb)3hq)JBS0o*{bW|vfLLZIT@AN6je4F15%q>Q3P>)m; zL=~gm8h^Qk=P70FrTap`Y6#M&ht}zA_$qV`N+lp()pztgHIOFC0MxQJE{0m-7^p$d zIweES3Dr*tvM6yYF$!1b@|H6fiq`%@*I`9y$2k?lf~gqxaT>kuNfInH^fcrfOPUrv zvaX$Hah|XQ25{;GXjso*A9qki0^xwV2ff8da1lEDwLHczBTH(|%DE|Rwv&30&rh6$*4R}qsrPZYScohK$1K==UT z!@d#lQ|hAhd&E;`?+(RGQsd=oq2gY*#rUY!^(Zwl_fqI6Xzz_RjGc1s5C)3aj0YcPt;pBWY$Uv*<5oCAi^)o51C*l4zC;LC7)7x$ zpb>A-SZ5lBx-_$1vb5Fx62DUJ+Lc|?`X^n2VOQu;Y{VYvfW=Fcj^gSKTAOxLA1JG8 z2kt1SdeAKTEm=>a%_F()TLuITQ9y_f!}yu7(izzDP5LV9>8dJ_GOwY9 z_^W(kDOYw5$csA{tNiea8oqYI4`md>^z!y2pKEbt@W-nWR^OHuk~tUWnnl^U%f4Z_ zg2y1iJ;z#kgX(ab^7L{9v%HLsiPl!)RXaE5WrP<2NkplZ!X(xvo-qg`C|ZMjfMVQ3vF`|7g-fJjO4jDCsvR-A>7&{VllUC-v zAGyRhZ9pX^LwBT`GoTkj2?|JjpopPbzY;8{9`)n~9@fIB5aH}Z1Nb>$aX_tb(uTxt z(6C1}r>CDiuPpr3FXh6CCa_;JH!-1%YYuK$`;rjo`SVS7{%`&|7M?3MbV>gLa~da= zG+V@vi#rlcc39&Q{X%Z}YhfX-vxorSQd;2U%d<>Xm+^#;2d;oN8fzp~sVpWmL>`_1 zv?m)P01?q7v)^75Aq9qJ)}C21B<58@oN-B%0bt0L2EvZ5=uFZ*=n?!%x8!k(;v_OA z)x;x9{8VQJP(!_60*2l>1>SMHXJWq3ZaEKaH3b6#1?Yh!G6j^Q9ff106kz!uV>Q3K zknV9;vkYqBO6lbQ&IpJZkixtJcSWXAbByshc4q#LIw$bn^s*^HJf}s^SLw(I4a7}y z5b+HAj48(qT8kX%VoW8`x-v>EF@>u{{b(wq(s-ZFnr1x`+#94T;0#>P_55E1*KIq1}Ab=io{0IXcN6_OW}%YKbnr%0H2H*gXI=T>VC7)3Ob zM&C<0Nu@f}j8?=)Wt($kz2!$9*954BPBb9Oc3>)_08#sKRhN+O)eZnY*l5K*mO30N zH~B&kdoEuskWXVs*VsexJh4=`sFaUK6TQ$N1H_7gTjSDbPYG+()WdTiw8pr4O7<*} z8cmV$B_Zl*GP@-<(W2 zWWd2xM!MLsN$5?DQ&lP>J7`2)TBSYNXFU|pS@%KBPVKs)#^5yTade@UFB6KjnYK|A zm_roXdVLU1{U2a%b}+h8)s)QbBKpw&^^7$FJ-mV0;EqmL^g6FakpJM!Hl7&j!HOaj z{n@g%FTdl#_QIvn4Lo6%_rlE)W^54a9xbYXmeO?)n3V6!H73jpmoQ zaNT?fldIMXM?iX&L_qY$MxzvNlvONjN@q;jkw-ae&q5++_5*ysCen7v1`o~qbuiNm zn(afxN3ex4jMXS$h-Q2hjG=WuENaIX7f<+iIM@WUJQ+8;gE!v@*M3OA>0KnwIT)T& z|Ea1^^oqess<-TKt4=F{uC`L$FPEGj!{6B8P6UE0VQ^qD>4N}Vs2d^SwJ*;l^wrtAD3@9ykRZ<%~g{%+O^{ud`_)M1(QFV ziz*ePG_n23I`t5IfgQ7XH_T-pFc+|Qm`fl1rt-D{|9gcm&;#)QDEtn~O>+geP32#I zHT(W6yuqCRdxgDv##DZH#fbP<3D!yv3h{sSeieYN{;ojh3U)(k#b^B5zqP&nuRoo= z|B7EH&@C|xzXAMb^#pF%fX`VH{%_f72olTt+L7l+=4PEC>0~^&F>>27G%mwcMIQstr;D2W2;cNMSLi|s3zmC9uCEV}p z^JBlJ`&Lr~yFJ@_{JBESz7!nDP^pzhG$Kxm-c!9ad3faJem-y6eedi>?qm_nF>!i4L43P1rL+9J&L4Jv@n7+BadCNd{rsE%?#Czd z3>8ZZu_D45&0pl&w}M0nEHgCU?LEIpO8Q;n*hTClKY(%O}W{< z_5lXsmzEy;;(j)nPx`=7ehdS(BV&A4iB7imn4EHA?BD6#eb~CG;NNO zfWo!Wb$R7^jInWu)w>^qXW% z74XPLxUb}hKK}~~m4Oy!&ImwXOga|n8{ub4$uj713m5_7JPf*%N)aNWT)tZ!53P5@ z_3fs=yVK3nrmVR2mCqLUqPDuw>n&e+n`pGjF%<<$3A#bv?z|3v0F_xyJN<4xX_uyy zqSQ--z0el5q}dRjU@Mfc>;q^q+^28Bj4oVKVVmB`7-`E;;T{cz0Ps29q# z;2CGYrUZ-Aqg?$%`BQ!=F2q_o09r0zfaW6{5D&6bGH#yh&&mF09q6JHgr=RK9H9xA zP44VgWLM<71i}$$Rh0_ZInRUpgla^H4xEuij_PaNNq*YB%Y;&pCB2>;VIrA#eLGvJ zhO#9h>abFWpvNi0F~y{lu$^GuM)Z_83k-TfqVF;3>VOotUkIrjBWBPRny)$#+3NnE zwe{WIi=Pli(q;p7`;Y^=@`%9G&D*-W%C-!%4oB&d$Ln=$ADN7aFliHo1AGFBHdBV( zF`~4@0H_|88jHjQt7{nyp){z%+w}6en-z@(K^! zZHg+N1#V)AApbFAN{6s>=n|id#884eI+7fR)npO@no><+NK&>v2)x-^Y1);J^tc-f zc54vPG~ZLzr=@k>^IEg;2UKe*#@y2KG z#BB%V$?nt-{uO@?%iB-4*hkJ!FYKDZw;ujbG}F&5gq#}e2>$nq!Ta(ZYz)83F15{% z`a%s_^iPbccH zK{?!^=my{5PWADxaQ@~reomLb*U011oae`?T+=U}%h%-wpW4drz?{dAs{HAnaSs;x z7?VHRdEr0Y9A*!`VxFG=-%za!0{aALPf$**$SkZ$-GfvldDTp5Kc=eB15AM_0&n0M z=1^_AV3oYWEV&JP(_8PZhR_%fsYVMW?SG8%SZ(;mzL5W4V?STfq(_`jTlzl}_?laK z&TOzZwV&pXHhBi3b!;NwnS4IIshn?;Ro;t_cy2R`jyXAxPg$p_lkO=XpOJSno$kMr zo;c1V!_21tN2V@N_PmZlzv zUG;OZ?4LvVD4lLP(PL6lB`e!4YL}U(^&^J7m(M-~obj($>?0KR`}ThRXzbCq6;NyH z$NBd*RszZ$0c?BecM6tj)-jI5UF&xTC#TFc2ItT|S>>BT_iDG)g?^^&VGd`8-u#2l zIoNA?_`$il<+XY64HW~|{c)wP8~)TgCa~cFJK|Y(r@QngxO<&3L*PsVvsL@7LmaeS zY|%0@#EuMKt?Dw4Im?~M<$4%@XDU*H!GPO*Ki6WC~ z?QK#SkV^+x&rFOJ4snsW0t7`>N;k%8BI}=kyyJqAEnJ_Ek<7rI z{>TcCYYb2tJyAQB;e_D`rgR#tn+ki9F?pSSg^;|1jGVsFM0WnJuea&OU6%>|?yonT z?-$0bo3EAOgs0ll_v>%lgPF=lbG7ZU5)S1Yn=ZPgMFwf({JaCMa-b3E)$70vAp={& zS~l76qG1wYc%+b&GZL-onKS3!R>@pj| z@~dH$gPIQa@$>RBs%chFBwW$pVbt41{k~4B zmS74ddo-qN<=nK5w-B|^F} zec*nyiUTcti4XS4*SmnX5Rv@6W`ehun(I@2rQjRm5M)-O!?S+(HIeMm^CMQQ_605^ zth?e=44F>0kYkfEM$#Aq5^-i}<#n++gE#%ZEB8;6yJ!=1lHqxP?XLtmI|UFrm5|@# zs+eUJDI!PoD zpaQVQE_jytKV%I$%G9fqhIW%%J3Gj|%8)A5DRrR+vK0Fx#4GYyn^mZk2?>F}nomB` z6;j1M3e`MA6KRX-9;AJr_kwvtG>4`gK2?d2wrqAtG0 z*+g~y`WrlsJE=~e)^k1s2j?rh#Wvj^V0qkqmxVe9$-mU{GQp&xzok^HhyI2INHUkX z(G4{ZfXy$ql9fsG&}$=%;!XB|coweosa)vE-IA*yr{|DMu_Bu*?5~L)l6p3(_GBNn z(;(AN|5Nwwh3wX<^>^3jQq4d+Q{`m)T`Q)YR}DmtvP^>AhK3-QhbkdgdqGKJ+aufd6H{MHi@ZwJydy21M=?f^RD0cS_+O-HPC;ha{OMMJc$ zlH;Di-Ri^9`2cFG?LD&I)n%@>7C(8swbM7j*##U6Mx`82rIH%H$<2p!`)P{Sa@iMr z<3A0XV99&n(e_t95jV+#9P+7RkjDFs4PLuieJ*`e+aw*ZSS@=DsxpNtk0|##d1_3{ z9fjwicO!TLi35M?%5`jac;IWZP(vp=U9rDqV}O+MQw}+kB9W|%aU@wYsubcHD{7ov zH70^Le;;5r7vzSnTPh`2x#`aId9TQuu~vJZDb=~4{-%O9EC&ZI?&{!g0s+>&tquq9 z2tHf5dh@Wrg*Du!hx2G z(8J;cr5Tb`F8iaGchw*Gn6`V_Eg_Sy@`(P_6F}M}cjVUH8tqY7xWc~(2gB2vRn}}5 z=Miq+LS)a!fwaZ;M9s}v{uN{}%+MbFM{In{zS^~CYRn#BWS&t4*XS~~Tqr>+nF?$J z$$HwFoZznFtG^AX+w5>YdQiJwi$7$enA2a4Aw{Y9b}&vX%>35fgNm_Mz-lYfo8=20 zd(a+NgFQ=#_DW8you{q+*M4`%e)Q2Q?gCD6oy@&DQRkJs{#8uxdp9tk{}Ub?3XtX0 zm)GLths*8iv3Cc`%KlUDi(p6j1pJ?+=|pJ0HheaxzWG67U&@L7Mqdfjb!C z>?@snrAAHCJ_U^2TXvd$WFEfxg8v7-WWWvJKkhKfX_=cRlHoT-5gGq8Yx9f;1QYK}NSun!sqP^;G-!N?VDCxAv15QgUE zZyr>Ncn}2p7ju$1qVjX0*E(5e)jEZdMdY@ z?aRaO{`J|}qqWKX;501p!bl33j~*MwHd3Ef1)|o3eJG0B{`~H47*=l+U2kset&2qeBA-zk4sR&6d zI1Ik%z`ChbBu~}v-pm%Sxx5ZjWOJSo{_L#|wZ8f#flAc4*HGv6-XE{Yld^HRjAWlm zd7MS^wXS+0pS7lgN5AaI5d(%?p1c>AE*r3zvvGS0VP|7?QWv{I2}8PsTuoI2PabXL zkC-_dj8Um$=8pBEiZ6!s63aXx9zN~bY@on3K&$bIpOSv`u`#9O6O zP-3#8-M%y|BWgkcZSP7vy--{i;_498cjLcW1sQl^W{IIRvd-t;$WsgUA)HtPv;;Gp z(A|J?Ymzx*&PL>$b4kEly5EJr-qy`zFfsSFY|oqrP&m&>ZT1Q{C%YO{%34T1p+Sc9zJ<{sl}`88>`N%;)CYy}6Qq z=rF!*mT#g4^D0MtvR9^;V8_XYOTxSZPouuduGtIcqZR9&2*S z@1r`lt7O5F>SNOvHbWA9M*5x%+jy#qZOf)m?2JcUv&ln{n3?DrT132V$A>#e%c_+$ zWjbgki?)*gIvGSucajhkt)}CEK)Vg+>7`m5u_jTzj^9valxcNbi?m<+qKG|IrMX{V zrp%&oABJ>{s#2vnTgIj`#)sCFJL^<3U|9uw!kx6AbUbvh&n98?`gkCrPFA$);sExj zgr8d#+yckJ2(||D!BoR|qN2tqu^b*Y(+e82iA0+@Hig&&%)z2Mw6Jrmmb9O4G6mR2 z8nbl^(FuLMmBfk!JQW05V|D5>y}{vJ^(1vRqbT#iNrf9bg)*A6lcX&xYWnmxp^j9e ztD=q7O4(PG9#ZT6XjiC&9@6skqk_)+uAiL+6Uq7g!B4|!%XhkG<+F=8H?MUiSZ?kT z`3Vc{7WwHNq?=E$_P@`6hkE7#x(@$2nw<0=#&NVncC&ZjF~XbwllGvOzfQ4ww;`J?tR~@R}X)z)w{8~ ztLxOM-h2J9)0kaAEksZsnE(3G^^c0adHjWa`7b01kyhdU`%UR@vZQ5hvc{Js{@eY$ z?1hvoi4VW9Ioce!uo49gLx^@7Oya9pj1=`CoQUJ2NOmWj z=-c)voG3u`=Q+z23Baj%{G*l~dG1&#Wo@vg-|+KV^+q4S_|2*s_~|iU4t((d&L!3X zJ#gy}9|_N)ZcC#A(ouXY$Q@oh%kkQBa*J#A*!xwMdKuxb&Z*vbqVnAF#SE~zoDZi% z`vc40&UV}pHb3%E{L{%|tthktZI}W<--oZQt)f95V5{J&Am78kvvCk_8z0JXUv{S* zZ@&qv2vpQ*J~f3}Z@k=r(TVl>?J%?L^`#zsY~S0%v%87b25!LL^?uA)<;4Z*9p9XK z`lVt;NaMRzW*W#yB_qsInZpyDZtFa!5cUB%HO-^|n#Llwc!(z%0WnVY1gQ*i=pFtt zAY9Vh3K)cKr_7LUZ%%f~KK7}vD0EMxxM$umsBD>ycgkAbFsS?mo!DLOl=V!f&XefT zOqwRWUib^>xUo9$#qk{61EoCit+K@D|Mv65zi8!1uSwzgu=3Y|HvO+oqSHVMU|sjv z{%Gffofz}J|)Mud{=xJv5*ldbwrdxwuEgSJk^`He9hIW;>_sc)}BVC(xY zTkfb3)y>{X5p6Um;RpS%Pzom*seuLkItehGOQ(EXV_z52bdhg7S5Q|@U}k-sREYc( zr@AWaue5wPvP@rL!~vN3gFPz@T03fw;qIp9iA@_kxV{L#1kps%tOq6_q=XeS8Xi!} z+_)c?Y^+r7huGnIs3l8aabYzpf@Er)Oo(JaD>%y*e@4b;+uLB7+(^IUzW1eX-7ahX zmJ^ai&sk{A1~aSNxf1rS8LQFxI(P!JsjT-)kVDUz5Y7m)uiPmlZ`j@{tS2fxMbF~rXQ(KV~Qb1o8sbQWig9Y?oZ$kW|)=!RA4m;h3&hnsb$&WtJiedD} zhnj~(J4Q>fb59D|C&2uUiS`T27A=+aSUpxewHak#yuSx^KE#8SlO+Y^LZqs){wlGn zGogycmg%DsHtsM>*Jg1g#B!e@b>%=u*?s9iPh~wh&JYKe zeGfHTHEbS2vbRlvsal)4rWSgR)`pD8FLHEAcIxK4{|tL>qbnkWH1?MEt}xMF`2wssO4y&;dK=Js01zBUS8 zo90es;NXv8&!JNml5q$)aPsQ#Z(_f4ksq#xnZ3IF4ZR~0u7r5_r5K_XOnZV^{tOwD zjNu{U(W%0xNU$h_x0*e`1K%h><6g*)o$)OVH0zr zPNNt+>3jJCLpODr-8^nTkT?%gKmsAL!Uva>B=w?|3&%Ppj6u^yi%B+o0K z2Mu&6jIUeRL(uY-6ePXjd1-h4X$$_AQkW0}0PQ(Ba~V_H<>e_%RQ?SEEz zUWIUO>R;pEnTycs{9#kQwNzSVbjdj|HBzD@Q?;9!CAJ+;MRvttpu@GzvgzV zhOw`#Mf`T0KlSV_tuN@pJw}$_1E$mm59Ityfu}~s_PRjs=*sfxYh19Iq zjVHK7nNKJJM{64q`(RJf>{KJw_S{_LCW9o$zimdC%DKv*3K*YC?$s%|J7MXvKNm&m zKZdCdT}w);plEI%xuVtb`2G5Y-f&(ZBPGOIX!4J1Qj!*d812J0cic{XLBenbg&4nj z49Wnb)*H+bulO;;RWr0LHp?HBc|aGJxVQH^6iZtKMn&kjrho_B)6nYZLIjZ$dP2#A zsFoq8Q_cailvRMd{4j2I5}itQW19w7)Jh50!#3CBYnY>2Hk8GV;r-R{?bgB0k6eP= z>+a8B{E)fle1Ug;uOV={F-u7M0xUMMZ*CFPLPQXBn3{EznmcGvx)GstV$TxK#Lh{0 zH2YkKH+f`A*EI6hdAzYY@a*FD8StK>6MQfpam~F-fs*g~Hc#_l=}3Ualos1!LV^z- zW#F4W@O0OJu!|7s>+8lFll4qHejLS_lKil_{M%79$%uWV3LYw#N>*~TmZXJr3z}SN zOB}_wXkuk$6lu<_Hy>fs(V$tO)+A{1;rhKlH*83z9F-9j8&dPalfRe}dWDsmi_l`3 z_KkJ1P%KE#if!}&O=&29^&d#$3spvci33>U2iwZ1Gv}%RK^WU>vp2gn88=%?iJe!^ z=wzFSam%-Q3GnB!_m^>Jvo?t&CETfA)o5r|+8iNR&WCoDRWQc22_k3cwy&SkY3t|L#kNf8MUS-1vGdErW*iN=t9WN$F?krLX+sG#d-EvFdO2cQK{fl)C@zHqf{VlA3e*YKPwa&S(4SnDHGRDp7 z+wz+S1cZQo!E1Y21psGmVR6LL071@Gyf*s#6g}f=XRU)-cwJ{P6rjI3cIvG&Z@b$| zfV2hxfJE0yUT2SccQjmiALl1(t;z6w=*e!*@D9> z%RY6#Q*593N?zBZR0CtH@3`hq2PbdwjZ$uP>6mK zK;yibx-{xBmhlVn3S=)HS3KE2Je-~bc&Wk7jdyStwsO@bjWpa&+|=v?uFkY>`-gZg zmT{9A(dJAPKK<3^w%&T;Coh-ToHOw=HEROiUuUnc&Q+y?02CDfxvNw64;Gzn$Je<8>xO?%yVhXmYw0GYtn~E$UPGJuQo^73O7;vl3qgI^t0DFsaD=G3P2@m;%WQU>lCpEFY72SGL-W7 z;l5MDi3Gjunc1u6Gh|UhJ$N@Izsx>2aLZMmh=*h%I286rB#BlcO?BQn9nyE>=P=%g zEKd5g8gk+dE(!mIi|e<^i>bz}^}#jnlRI76dCn1LZFk%3Z++&LtWOf!Limq`IbMI71X;9?jY&bzkFQY*}|(zsln>BK5JI=d_M~H%>8I zWGhUwyXWZSa=qs`sI$%FoKNu|29Yta0UH z5+^9OwR0O|57-*TwKF(b1o5Lud619@0#k zjn-w5c_UA~D8v%#E14bXi+e^Dt(hJm6vJ3fSN8e@D`GG?X2+YsR@(nit_rS*SsY+^ zA~J~eu_HFdvyq-kBlGm~x|`_Nx}5Q4@iRNN4l&O#kN$R`5n4o!<#4zyS$;&e>h;4o z<*2Z{lChlQNA*!71r?JN>m?F;{XtN1P!+-oUqLUXuSJ$)G*dX+_sEp4k|+4PPd2Z( z2E(Ah)kXrBPbVTSpy^caaUk+JZN^ zrDmDqq+DXw3m!PTH2N0e*XYD%?3#x^b~opf{1&Ls%lJl=9W>k}FBejN(q+06--Zmf z$if&iAL>#gsB&~7E2h;ez|D4MB++r=wt8U7>3FOAwU7nsmz#IN1u0gykrnXgc+mf~ ztau|bA1POF^dGVO} zAArq{xF@b9RN<)X%h}ckS{O^bUZ4t+QS*JSF*q0h;UeDxs@L4DUR;J>qRn^q2slJ6 z$4rTg4){2F?JepK9{j`rwgUezF#O+py$TAKZh!N}RN2}ATK6`;^uAT&d>CAgCIM;T z`;4L?sF z9%_GU*V9$anGr7zw~5u2^`j4K)u!7#oPgUacpnAGthAmx=y>(-VFkq}f5s5eFrcVl2u-=JD&? z1KZLbSCXjI=#I=CVJCoGG$Bmi$(OSrAgnJC-U*Whm2P?jOkX8)Vq$_>ieUvdMJZ?{ zgzm4~u0rRX(OOMx2W?LX@Y(U-8J8_41!yJ$L>|!)#5&qRDMX%!S0Lpz_{2#IJSB^m zK0B!}HJsI32hVRS`DrXAJhzPkRHyY_o%EtlmdC+KP zz&nJIsX#q4R=LQ#n{t8^tRAf^5=tpQ4~MrJ!BsmGeC2&WawE4pV#tAl`~c;8t~bnC9t*VsJJftKtv|<-HX~$>kfVfrchUcVGs8EV`M{J z15%0MIO6v-G8RRO#_2XU-1J%c!SInBwSBU9P8o>m?7JQFCW+%w*Fz;8cQWmbv@_^C ze=@QuH#LM!9aZAgQBPL1b|-#5rK{Xf%JEz(_dY$;L!0tsdWA{z=IKzeW0U$&H$vK4_T)QDwPa{ym`x`g|de}^&#c=2WkP|D22Seg|e3iY8;<8v;)wY-Q_Zc z*PQt>c&YRv<;s&6*P0>P;GihaaJ&#yxwF0+N>MIh@Hi%8DFC)WogcoXR+40oTucZl zv>EXlOj^lFP$}Q&Tmm04dI)hL44SNUJRjZtgA6Yq&H2shCBW7s#d`8CMk}cy-N?2C z6SsX>NG!}09W%*{Ox?}&?8!9AwHO&}>NH(wIhDWIXRh%(ZpC0d{by3xrrTzai=$lo z!1r%pe?s^%u0;ImlkwoOn55v`sM_FQj7b0V=c_W2A!U$#ONKWj%b@7-&)5h3E8N~Y zJiq*8xV*vM-rHY!J<#j}Z49yvT6fZT{SPMlDDzkez4uM*eJz-a0 z1d0Inba$-f(Jc?-`F|8-X?7wb2kQl%%zuEcr}W^wCcd%TLI;Jw)5z#n3aBTa((+E^ z9g~mz&(+Z_m!`)CP8`ss?x2S(fa&Z;od0+4`0m>VPJt)!;P`G~fu~ZW*8jRc=r>;N zzCo@Hpo|%?HJ$fHa6;swWtqD4tO&tiIe+$=e;$u|_k3+%_F(}0tw0p2v;fc2PQk>@ zK?O7PjH8(v5vH0`sXr=p=;W9GaEHMunbInEv z{^&N3OTd;NYae;Aj27H^fB0ZH6~E2?1>&=x1Npbs^E|$;ISiv;dzXbbsNSEWFFtF| zy8A!b{eCg~Pqnjq^p4E*pfnx7^KBo$_ROSSum9xlgA{w**l`nb|2&q(ZsAn=Tpa>l zf(8XyZ0CaBpWF2PAhJ?(aNF(KjG9EI*QLdyjK!e^e;#D7#_L$sYl6iyU*|JKy|=p( zv(_BJvDer`#)^RN4NB>{>K>#G!v-Rqs>ms3JyZMOJ zO~=ZP-Ok}1n^_J-H7AO2S(h;1NwiJ%Z<)C?%pism1f7-w2fexjrT11A{&y zj{3+_6`rl_=G!P++B~=1^3Zx#Q)TFd96@LR`Q6v!mHg#jLd>baDusiPVIx4}PiOuq z>22TZntm$)!#FdAR@iaaKk}{l^h+fvx)`1@s3|CTdsaWkx08Nw?U=@i3^2#Cgp|>x z??Ea!CiSn$-Rd5Tm@8E(sVd#pet3 zvnf@t@(nx2a3e^U(%0;%h_m*Ev!+R(RnE+R!i#b3BVq~2X=Ol(D4RCUTeP1jdj5{4 zPrw2HS(~=QR(;^;;1CNVZ^<&V7!r~rC1vVhFL85v7$k&+a6+ycPhCw_L@Xs0!IX8( zH4H;Z_ESMYVbVhUQ8kiqs6^4))n{=mSgmg_brue4_U+(O?uihgwRvh;Wo+`^<$;s$ z?Q;<9F@EfXk z(QP9eG9(L&cWHYOfXnvsdwq~cFO=arKi$d1-jSo86*4`etLzfpuH^SzLe7_m)?|TC(5fOb! zFzGNFf6cy8vywT`j17UM_MZ<18%UfPY@ZAAf1C$D@Aq#L`Z1Pj5i=9|;io>BeeAx6 z``3ItIe;yW;QbJs?-~|@d!<8UORzA^7mmLw??8$E8&jZ2_6)8S+~_3d^Oy=tIe3=L zP;4vzb64+ncCZcK%PSnVx{H+Sf;Rpx?Di`Us@2kP6Vs(#Dlqz~^syc0)RDA6(*4&q zyYTu~p0Pa9=tx*K5aTP)TLR%7xI0$_X8#{C^xYps8(e%dI6w~XO>F`0UDHqabEA3= z!gc!J20!0CQ25*50WajfwgwxW58b}tAU+>@e&->%y!)4iL_@iV!tXEv-5*YFZt72g z+}s`quf6L5^dJd12&A~m`;Tr4S^wf)c3zex9SpYETV4GD8XkJ@c*kj_j7l-j`J2*l z5BuDL#|}%6ip9k=WVDk+qwt!|_kbvh+V=g5>8W^UQw+3ODeCFDiH|YW7N)H^iQRD9 zYDB-M1$cUxIRA*E-G#RM8rd0StYaQM{lA)592{YA&a^X~bn&5BRqcT-#ZkrA;#zI* zGIR_0NihpQ8BWw~TxS-{m=2ItbROcb<>TS-C7gc=aS1V!w-p33Bwk2teguZKEIF2P zy*#)rpW`jIqeTZ13jVe0d{Vl|5b!^75)Kv<20_*H;KdzPU>RuXs%^WAP zB$#`g)-m1rdKtnKpm;|#*hZ8%TV$SGe(7qI?{0y=rRg4apT%vjGttyULy~)b$k&=L zEbhzAIUA+ZNAVj?hi>F^S;Zb;g)1KTgjxL*dB}gKt_HJ($Q3^@Do_A#0B^G&DA}&> z=#{>u<`@XEOCMK$LN7@p<5$o)W}~3vR2Ov{ezt0Mj&nq9r^;u=B5VHi7NhDAH#s(FRg|*%61%4x}-AU zmR#DFmUr!EjG9r%v}B*`p}?8JN6swmt*$xVXiLc`pi;x0CPghAgX0|b%+*0FQf5uC ziIL78AEe=z{wRXyc}uqkzHIhx0lyTkCH8y$ef(Y7UXOvkFAENM%IG<_m&)}jcwnBI z^td{J4)@BnQ|12c4im%cEu5fy+|kS5AL2bJSx*{!uj81rZ4~I}RAojymC~8kS?AFB z`(#9kiD&V%tU-ej6bG+rLXwuZ{gHQA%p8jZ8Z&hmTr+KA06|L4QCXZd?Ic&;HOtr< z_M5|i23Lopux55LlLfiP4l}rED4T3R?efI^{eoOMVnH6GuItHTn7%zI>C|xQ*2m-p zV%HtgtwCz|^j5p+TZpGo2R_iO!4l2~n8?ot=?&!k85d4B7mpr2a{88Cw1K=kIUtWM z#ys)7CI?QNW;xowd>FcvB-x9n2@f-|r8p^K@&yg^^alXl9ZM}JHwIzmN%1r#|2=tI z_#K$<;~00$uzh+fuQIOux9PMmStlB$R5_Ep4RcD@TlpNB(ac}N1rEM;jj_2VEVDsX z5C0Oo8mwR^D0YsQ+=!TG0czR_g5AK(V`k*%50~hv zHYX^}aji%0KQch9gYpDlJ%__ zM3jMks>-}FiT!7atFXt-oVV;fUX|6loNs*UBUU)R(%#tz1VbMWQ|6IJN^~~{1k7e1 znG0~jxOaz$1G)`-C3%7|r1ajJX50%34Zp7pE9rg`@4?&3cq)6%OMxXcy-{IAvR0V? za5j#tuynp0B0wnnbmFcuD5hRoSBBHE4xSq|{7SIswUbtJ!PVBnBKkIpoOyf1%s5rP z$I=vAKHjFynv65bV=e`GjTEoWn{+?L>gdSC)77EO7^55iz`gm6JonFj6svlM?V1|9}F13lF{?|HyFXYFkQw`VQIgNUI}UDGi26-^!Y5dqy)8O*N!F z{|xYtoc-S{z61^GvRLe6DHNh4`E2(kDpw5K?P(z|2Luf(}jE+vcG>^P=|rg z$~Mcs7k9UnQu(}Mz=chn)d02n%j^+B9qC=u@9yom(fJk-p_hb4;56~AWTS!Rr5Qqg zKr$RX(ob>hhhmQi(lj*n9hAdJp{*=TUaS_+w%!fbnw&-d{%7!5D~+A43)7arr9?0w zzjwOHJ=q1IW^Xr{QBz=e2&z2OcC46PQgN%g4qEFL*I@Mg{b_*e$zjjXU~mfkTRO>d z^$kCejccY3oP6p;$m#ISU7-vyIH%N9yv}ZK5gr4(O7s%`qwrC&nZ7y!c^y@elq1`q zAR1#ms;2E5gj=XYf-zu<6{gv-Qa32=1J+IWELWK)ud14m$7=D%tX6izEXtuu$q@~i z$7*Yn&912$B?N{we5PDafow(Hz&#oJ4PWH36raL8r^QV2iu~9;*)XL6iNfC5h}~Gw zif0EW-|EC0XKE$4Irx{?OCGSjXrY2nK_%@(z zU25|B5G!;KLJ4YL2!G0Rv1w?G?83DvF~YBT^>sI@wh&1y-b% zT)1#Hknq(bp>qpvNft+(m^%lOF!v}wOSNbJdK2^8#LJ(V(yyjXpob7|0!wiW=;$Wk zE6%#I4HK{%NFM7@UKL6X?5aVreI6hVr>!l85O4c?u(F7=Tm~3-Lf@rHhc~Kr}nfPmrfNpEemf-yIN4EO*gr(JF!A* z`kdMTkEdQ$G-sFCC$~PY;(o%ZtApI1v~sx~z?n4sy5&4{hwUBxL13!Glx=<73HD$3 zPJ*~A5}9i7PTdB?7v{rQSBY7@gM`%jMA<(mbXZo|Uekl^X0EjN6Bcswa;xT5zY$m; zMXs#5FSmeqrIQ0qgWXqvH$!l?QA5 zy4ymy74NYAV`amG_u!v1Ksyu9KW?9luEhUQew7yVLIJTDCCIp8!N&>bC3E z)MMy9J{n&mcAWPHB^guyhO09Gcgp~JceVPnS>5?3kH+%@-EsdF)0`98bwSfvPD{VL zcB9z7npkkJoK52B8hlz9U@be><~lF!a0BYkEF#f{CTkVv8MY1%BlH9F=gevfL_ z5Afp8%^~L57~BU~~Yz2Dvib{T4SQYP~R2ABr`cU#BfYYeor<=lw1b zSJOlxiwHF`R+AmPSqr@muL~W=H3f$)HI~yeJ*tn7^TJOJ`FGcIaLfAOJX%ADrl>?dhEH^qqi!gae_ zRf1tmLW|wGt60)LiYXjR zIh2C_X~659+K4IvL!uYzBb6L53=%WQHQ^T+a<>u$9{Nn@NbFF{{~?!$$bE;ZX!%}> zKXpfzFituiYo=dscHP^h_8Mub&sRZL1G+?1 zg@hC)b*8AR1N+5h4pc6f1PelOvN|Tz5g7)9r+|0l%wmby0xMrulyut3aABlaH69XM z@IG_eR-+EwBX{&V+*02rbP@~75gpO_G^BpBhu>*5jFC90NF*mHaOar3fnE)<)G}F( zx<21|S%KH%=cRS^ViN{FK0MO?u83#dSNi9PA$9K{Ou2Hc-jdA_ZllzI1y!E^QaT&a zWj#`54ob6&|87bl^x5w{tISCr<+BP+c$gK{=1$`5qHKsNX{$J9WF2l%qe)ZycW+Zzyw7UV@S^Y7iYTQe(CC?Ft&m(vD zwsbF#jziz^d}UqdkyWIj4^qAKD>Q;M&OA$UHO`uM?v>6?pM6-hyAiA}gv4HgN-<7T za>I6!65K{g$N~&fBAEF%DfDYpj&`=V9Uncq>*FuL>ErJ&G3(>sr&gIvjy17u#G38o!82ouiF49=bKGP7*wRpS2<||L>zLHTVSuOmoRRHFRp9CNVrGkooH_C8b zh^P0<#GVHA=kn)Ow-73mm!%v1s_rJ&a7U@}`mg1Ew1hT0sqtHwlkY*Uff~n;{g6wZ zm~4mKar%F3>os%Xo^NDU59w(!mL{uUI{T_%y0d2gO^Tp5 zq3CM4D}P(`vS+QkxrHT!(ce`8tGj8}vTsZ|%)!&RcQ;tSxvyO+si7-5@@K8<$1K^n z>oJyVV?9*1?b{w%R2ZQUrLzHQi|f7K>C`|%GNL@zsYvA0`F)3XEa;ja2BQ(9fvudp zrA#8uA^Hwsi+oNNeY{i$R!+@A9`U#G(wGcSxSOU>#-LvH{5k9-mChT5BrAaKP&+$c zZ>|VcL-B7>uZyPz)Mtw5@(baMlzIuiC6nrcr)rHD-|XIND&3;7omN#U@9fp#>nn8^ z2NIca+jbg%|Y6T-WwNH4w z3?rSlLygYMN)|qROqY7Ehh%NBlkK;rLVUBL38(opk%80eT8dwR|EzqR8w=;=d;R(- zbQVga)6*|JG7XdLI-rfrOR6i2EWH_i=HEYqJ?cRD(v~pPQhn-0gq4rh$KkR}y$Z*r zy>d^RfRf;Mtn?9lLn0P|b^Q5^`t%zfPwetaYzBu=aj5H}qcK}}aQFu%kB9{u*|)1A zMpCv1ZOtE(85ERE+)c-m$3%Mey#_H2>wv~TxWa$f%jzlPgms-CYIV5a(y4OD1=ah>aF zB7KRV5H=%=4Yf;sDfA56V?F&xhv_bNWcpq%c1lZ`*+k)FRn{Ig9CD{@QeF##pIke<2fXk1bk$>W~VY((mppJ$&tJTMN?B2ol;s(QcYw+z8Dl<3lM((9ohV z1ENo%cHYeC>uT{P~6*S`y>UfQT-3vc8v>W$2G}t77pp%noF_nVG;xjAAxNftYUC;X+uP7eG zSK>qdMye6qGw-XySLJS$bK7>?zmxEkD^K0OyH2LvZ4%R)k36Q1O^H#P)hyP1eo@>& zW|IE5>%{R{a-{Y!F^>f!QJBWqO03n=)K?^{F7dFvo*%)D(kSf{Iw|3k+s_fwKvUkn zIt%HE@z<^bxQ@@mU*lx(cRs?ladi-7=VSo{qz#1gi=HjkJsJs8WYNnNd+!WuDo}>^ zF{eSc?Zcm-yy90{Hp1TjK5KD6p4_LFDnRoS-C98Xk&##{^h18#tkozzY4y92p?IBS z`P-)Of-bZiQ_7!8CH;&Z-nRj63+5xXKBXoRtpL*g$=#Tcoputvm9=#x?g*&&8UvH8 zS+4j_CJq+{8L@OFnjK`Zj#hLJ~+S@1w7ZOCHUxys0A+FiQ zDV=zmGlecHxX_2ky*x&Ry|wn+>vme35GSYzlWtTaRw2GsH(wp<{Mw>UR%nTiXHBq$ z=1UNOR6{ymV@adKbI+j}G9Oe(K_OzqBvbQ>GOp`G5Vlo;j}vhC0hcOH`~%5NAvq=t ze0LXDUv`8pGQ+38n8R@vr=|25!x~aQVnQJ5*6AU#+Dm@0&vs33tlwl~*jI=k3}FS# z@sHQ~E@7#K&0u|uZHrYTH>K4b_~&Q;yWpqu!%ZC$h~tO-c{rFy&_y^X*yH~K2nQ|# zZ_C1w2>D(f%K(D8c>+Ko!moP(S-jvF@a;I*VC)_#?Khv#3qbHmwmi%(>jqgy!U+N_ zkR^>kw4Sr_R<+xMxd)|=aiGDl%+x{Z9$`pnh~fQnj#t(G5N9OiG7N-GgQbC+j_neb zT$yQ9wn_{e79~|*)sp_Q`79I+TK53owz{<6{~?XxaPz}DXw4l~!QAYcO_Z)w*ZV`G z{4H+B0X-RVvatirg(43ji`a==(`FnZ+{UTe$VYfyhDLABA;_{TMzT5=j_&+5w zmy}5hj8dcS@p}Uyp^LoG(&y(-SRN7Qj3=x>zBWIaJEJg5o>w5 zFJCSZ;KrzB-lx@%$1dDbn@UWACAKIW%(oWZK2t_D&9UiRID`J#X%~<|e{g?T;n8ciKTN{xuOzdclUuX`2?^xx7k-49{i`RC`{M9(Nul%8IVxRur8 zJfgijcB$h7%0F+M%x-3JcL3I0V^GQ7KU%EuYzBF%&4U9-KO%GcJWaf%fzvxOam18* z|8)-4zW`cQVM66~9ptS7k_y)i&BP^;6aRDQ>mD|@mXp`jOKIVYw50xa@X4Ps#P4Xv zoqg0ud3`OXkz>m1%}cFyMaD$|RF8Z6+OmIZf~Q9zQykujYyqybi=paZ?#ASmag7gl`^{QP>^Z<5IvBR zoVO%@iE?T(3{2*R8I`;vYJiaS9Oe3U4UvEq1wt=He3CtdOC~g`HdqF0hp^}< zxs)7r2Q!n6eIBF8oI&0)h);xspPqVbQs1Qp2+iKY1OM^WiD~w(emjFriD`~AI5CisHxr6#~U%-0aXBT7_V+=Z-C=&BuyU#JYNcT996aq`}(tx zK{*u?lTSDMNr4uTh0LNyE?e`2cskmZ9ZB6kVo|_^r2wfgl3>k2&ei$ln9!Z#1CJ5n zmb-^#d%(k%D)Kb6hcot%;)~*X8l21a44P+Cn`RkHyCkf=KeE1hLD`Q3O1s=e5!iNw z!mgA?)~rc<;A&GRj@9~Ij-c>!ynxP5gDkvl|N8Iv`128ZU&3Gh8 zmGQ8LBPHo|%%j@Fmb7V=AK`)HnhELMV_$v5wA$F{&%FZpp9t}Oz#E}Y6PCA$(chGW zJ#`<);eNiIi3zS!#N>Bl?)+T`Gd4{ZDV!dNQGFYIu{#AU_F}c=Trhkg>x0d4RswM!EmwzsP73 z8rb<}$4_ib7gW)o-oxKf<`lruyV}TZcz{3T%Rq)gdTP#tQj{rM?FZPIhsa(D$U!48 za8mCj9#q?4d?m<-33%*e-(<{@)%H}oOsPl50!lSXmok!Rf2P|1^NssnIK%W`&b!4c zn<;bYSaim<00`p*PCJ5gq;qmn{ELJJ$*l0QtS|eeV&4A$0HM8b6wP}A{x7boTbH-t z*Mwiqck6@f_HPL@edWO=SGJ{Gt<&2FoeQtY8*kHjUPGdA2sW+3LD9}4Gbc~-FlxS! zJh{ed=ti&eoUhqQ2T`};Dv!bdUg;7+_^AYAJiSW|8l-jCu0GW`jhvmWjEWuQ@%6h@Oc{-8V{x2deL#VAzzF~z-g*`i$-s`Pwyr~9 z!fzeQef0Z5bd`%Aerqkn#0!O9r+($vkmKC6J(x#@WpZ9OcQVSfArR@sx&J^77YK6*8UR z7yuSg;>Z(-2pVZk^a~`(hTy>frC5;7G>r)-b@Ap})U}22lyOB6zf%qmS%hOApUhlR zC_p>-q%4Q$0oGHb362eJ6N4gBZ&3N@7Z}o%YLKCw1o}Wc-t$E^PX;CFKG--Mq01oI1(Ze9s80_kQ%;50E zSp3f`FMLJ{BlMzlGL91U`h$_Pj@b4{v&wT*e^5e|BXg|CDysZsiN`Uhk10>{G*3Th zN&(r{Z(D~658Wx|-vxz{(Wm}wh6$eG$f3iA{b#W>kf;iq#-)y!N#lr*h$r1ag~SX- zeZUMxhr&E0yGoKnNb$u&?FG&wnQL)1Ta+1x6GjaGx2Dvv0tFT9mpMR#Z-=? z&3Z66RdcwO*{iA`O@DbG@C$M6wb#!AhIo9Fr05=^*Zhf5<*RP2zi3RK6$mKmCd~t$ zgD6Ay{;qgKvF5-xT$u>E2M3aOP&stN08|dm>;;uWi>v;=FIhVbYW4bb(C67rl&yE9 zy68-~?3q(;*0RX%{6H8RSi)C+XCp_JInt91$v06nRR^0++5do!kXcFh{J?Mu7gmkX zF2?}=#~BsV_U0=GP#qtQ+iX38`Har~2BQCz&iW87jq70mWO2qU1 zubc`H-tc$>U>XOY@(Trn*>ax9eywGg21D7l)*FAX66Wc9#%rXCPd28njJVx{NgAV% z`nh4!z$J!APBH<@A($%IycJZyK--Syys`)-rIrfg-!o@RuioM927HLm-fNoNh>mvGboWX3C6n;UQoV`}EC3A07d5k0 z#dV(Y07hdesB;tTjHYv_8n=3zYq?*H@L_NqHgv;I9So3ee6Fp%TA4Aq@-m}APsFCI zH<=m?yW852<7qUQeW062h1*!D?E3 zc-^zUOE29KjTr+7K9F~{)797HB8K}1P zW5Q8&N{VJ8FdSs(x|pHrgAQ_qiAiKu)1)m@LD#LkPH!D8bo5M#iN&|R4JR9*l`wvj z^--A}!$=KbxQxSOIQdQHz7iSNkx1i@-dRUt3&8Y5r?;VyDJRCfTR0=dKe}0yu~pl) zlo_&I{9Z_($J#2gk4QEe7!^bI#v%2K?Z~fB%0o(YOJ%hQ_A2~K&pAOqEsixaR0YnD zjO|dphJh43R}P?@2CyDvgEMa(d2OaZOi)BpbCqC;%~3TQ0=Gyw;}I5=PhUm!0!e03 zKm1u*x<;Wd<%TG;1z&Q@pOGK;w0>ix3*|n14QcAt`?76@F84=WM>MWwWaq4|*Yt{a zfv=+;DKZk=dmXaZB%EG$R+hKR_o@5GKF2lJs+{T#DDrXQ)V}HeVp25gUbAg0W&BeK zX7I~G1)-on^cyts%mOzQ3FJ5DSd2DkbHX^Y);d&~7opX@i}w!_K( zaLu>m)q2Fe9S{9b+l7!n{RvX9a9%O~XSPYfT-O{@D1}d^7j{t$Vws-Fqq*sSYNtQW z@6}t&W`>*P`dJ;XImrtm5GMSJQI6HhWtL%i zVu`7FFeYuqDhI);dm?{ghQ|L0Ox28$olBB}UY{|rn$}zLwa@~BLpdB&Gb2anyzGED z-OF+ch4>C>3gb<;5&B_B$&JNin1pem=zxhbAZ{8gTw%!dzFSXbnDlKD+YiA9$TVty z@Ob&;djIj6=I{jh@a%l=enYkO9nizk+hZ4tJ`L}4$9;cQu*KiB*6fb-cvXO5;_<9V z3JO+GToasaiiYnt_C#&*qxE%$x8Qm|&lZ^dcV_K=>Nl$Ee^;>ocffj~&K>Cz{J$gJ z-sS(@4X4YS>h7ulB!>U*8ULpkes;>#_L+N45Xtfw2ycmVkfNXxB$)WHHnvTF*Gq`U zaqkaOYh1m4L4e-?Xh0FItpk0!!~4!tS4Wa%x|Rh6g~zr|f@)@k9S396@Ybsw3l@ZeK;jM{51&>$_z z-`iF*s|WNJb{N9!4+78Q==+X%?;nr2{^cJ}iF*A%OS^wjxR+;{lTXcut)1_4lr zwbM2Ia2$S55WV~w=k3}ExmBNUuznhr70$l)dFObm#IUcr#{z)%8P_Z4-|zpgrYA1$ zVEeQh!x@MV#`G^AtSd^WL1irTiZu_+mg1f}j^UQZd-w>w{}iHBDc6o6aeNFu(u83- z=g1UFDir;DEPB>h|Bv>70~3eG!{VX2_!S@dfrc|<2ej!~cnR#)#Aoq*%#<)L9ISdG zcCY;VVTUaoSMd}YVaX3;S!aS+Y7^xC&rX;|&*7td^7S8EGuAUNDEFZP?yNY-F6~6r z|AFG)VF?;M@l~8;!c@{MF)X3TWeZdvisrmBm8Yf3G<8PfiFf}G0H{D$zlYc~W#@UD zoZ^HAH+48AK4AWWg%K2rhC64;3yz+C2|W_;waM$Hg-XxCBheshG@yS5KC|rtq{8) z>E(h%v7o6K#x`ZSRAl=4$9zegVvVWw z5VWgZ4}HdC91;)!f#Aca7I?DF9E)?lU@7IstlqAfN6!~TQ86X`Jt>9~>kJ`LsC=cw zl4?s=$8$J~m4NB0pqN!lwLbK~^3{44ILtB@P|Ow6V;I9aLu*JgJdMn$Mr(LHnE_dV zNjJ|K&5Ay)=4iSTsJvjNWL2$sDmG6dd5KdT6Cf(J3Je)Z%=uF}s~zFZVBad0p(0#B z-Om>($rdac+%04FU;CWD)|q}?ZrRhKMvSIHVsFGl!1eNgZm1_%-U;A!9CF2hgkEx- zhm-Hdg7H=-6O|9oj(+DR-wi+M&o`4LcKs@d0nM@xW{Ioq*_%9`l2i~P@wAg5 z=k!7o_LU?;g7ciQbolIR#y~!v;&IlU3?ap~M2X$Gbt+qAIIJ%@Uv|BsP}SwOU(rMr zliA!edTr|8#LrDoe{!`1eTOU}V$LaFo9ruHfJ$JlNEn`}u5_Um!>LAq79!| zJ-guV(k3^C1bQJKY6$* zmPt+PC2)h;IeQwq-5*P=MAefkSx^3VVK@TWgo_vc)M!KqTrS|ixkEkHo`?j5v*nNp&2TwO1!#)~l^UX?elo zftSk&l>J)ApB6Idup}#HcWn(lIG$eM$WB_1ZSjAix-Cm2x1@F@v~O$$H+e!7drvM% zx+(^&o*Nv7#hT?r)P4>Qa)~L2)3jkEsJc|!gNyHn`|p}O+C_VliM|yjro?O-y>}a* zP5tAPpef@E!C>sje@QiWT*!CI@;}%)O@fr*j5MZsYs#dADFfSy)lGItzrP^pUcw4j z553j*ly9~;5ltg)aU8(cNV%;HUx5Wl5H2hW3!I@u94)lKVKFqs%z_k2xgwnryOhdK z7;o4VBlWq3*)NeBph^!;3Hn5q9OX+WAdNZM0s95ZIm(FiT)@NXiHdP9!s(nK9J4$D z*%${ciU)cEwXhjWQNoZoK;2HnCA>`n(m>Qy5LP&&qMs_VXfm%qYYRH#Xo?L|H)kl% zv>qelI02O6 zhVNwOkv*EAj07wRum9wvB+tk(OA_gPWqsq`iSO$842oWgr_%QA&&E}s6`tlBF!1tV zW18vnpi?nj*GY@rlyA#QoV)3YOHN4KJR@D#de>5I>aK>nHAQp-9XFwe7T1VgxLF`A zbbVSw{R7U((IZ5XfQ2MP(})G1GVz1FEm^rAIIGl@R*t15?W@%8HCv+Z&GO%WU)_^V zXyS94p(RNL2`B$Qd+)j&$BiWn_HRA~hTaKF4p#9ZQPObt+YXwdTU<*rN2J{oqt=?J zOjH%jEFc>|k}O-=@H_0Eorsy2^OJ1g0Lc3VSu9eto%mr>m6->(gM)KFA(9Cc1IEgz z7hyV=Y#W)5xZTCEBz^0px>CkRHI&A}*~FFA4N;rb%S3&-0_xJRPC=H=-pX zQsGHN<-EOc5b{@|^bp~FC0l*qdm(NK8}+UbbLZ-vT?ZLUVeSO|PW7l{>S6*sJ_Cdat!Gh&bNZ7!p&so}kG5eZLp|EHtkF=9wxtdA zXk8oX(H7fKk2Za2s7IS?Lp|EIj)r=)_u1<6zr|QeH5WXY&F?(_+wT5uCI9d9o##XT z-+O5fJpWq(5!aso?KFc9Pm1~9a!+gXzvahf2u>mYTU-D>x^EUW9LI!(Z?Fn{(oP*_ZyUUJkrRdV5g`2=y3xS3Yl$n0 z__dZm>?~kqjZnQpmK`76x)%7n1>RFF9p-5&h>ME5FeWVq36of)U31|MTeoD{X)Y$aACgov|228eYcD2r(8-piELQ6JU|yJh%dI#g^Hy9-%P}T z{=X&;?*Dri$jegEKGP9WQQHoUAQM3A3wo5DFLay1x)cZcCEqPOU&g6A7gX_E zh%@-*;w4{T%Jk}$m&5-E$jWM}jV@(hjw^q|!^R9=mR`Jd_2Q~;_~?~h<|$?F zZkmuezM@==YiPq^6lpZoJ?sC&twO>keGsUO;Tyz-{R&;=uCEK@a>;Ej)B2rj%j|g= z)q7u#kR4jX)5fK1MUb|c5&H8G=`? z|Ees^q5kV&DIF}Ozi&(FQ2(`d=Dq5_0wEphzdqDf<^S~I!jusagMR<+{r`5JKB?A! z-F-grfA`TIF#lIU#I>@3M-KX_hF)RKS8l z3Y52@N?cuQHd|{#bXxCnSIRwS-^&f{C&?KQw*qOy%uu(Kj#V|)jn;`mm;aY>ln&-! zLk1E^?MI+fLsB()wi$0AM@gMVyvrF+qrO)#|2O)Kz_H9`4d=#S#+Fe?PnKxGuSjcp zlz_*}Z0L=#xt&UO-k4OVF!1l<^02dIYcE{TZYIKE(-cQY5WNa5kHNd5@p(F(2Z1 zmV33EK03%XLmg;leH&=zKr_2XFwo4;OEcGE(Ei-Infqw<@xPQwI-9HYn18$Nzfbnc z_TSy-yMzAkKH3A;{}m8%?XkZn269R9mwQqh^XtY_()i^b4W{2g_crL>{&2dt!SvfR z;@48q!SegCTb2JKHhjj_{=aMc@6MAa&&vGY^PTtR2ds+{%lN?3 z(+V$G9$SeYEIl50!ht6oc*21v{4?`}C5k%mg%6|E#DAE21Z&d${l$m>+bQ#Z`%j+i z4gBAIv_{`1Lpr~5OJ-X;42INZw`KmC#*cL zaEH|qmiWZV3wIn0GBAw;(>O4Vf58KwkFv>Y!)r?Dx4YRcsyX%QeIUEj4MQHe z*Buq$`WA!nPbAx&HncVK+@EyvZ8N?r4W5|~s@3~{uGa^ko&SIGbmvK> z{>$^dX9NF#AMFA2|Fwv?c0bT|KY*GyYW+ex`~hlS86*KTNCJZ-Fh~L`c~`dQxb7<% zxLvH_Gz;T9cJHNhpsCgghl2kuCt3~9S;dK#xWX>p8i%1EvXM_N7Hcu@W;0;!^GY3) zYs^~8_S-3DrA*L?M6t`~uecud6%7fx2-D&dG`?sJF1$z>y}3Y=2bZLJ7+tf;T@R3c z`(S%}TP@;k`{P)D+a!Qc_L{Jnnm1QV?T*1~DHl^M6=TuX;a8(zbVC=(;s8C{-+!{- zJb3d``_P-q#k9wySsbXt)YW2kQ!c9OOu6^;swaUm`4lvBF{7+l#b^srX3{MKwXO}p zG?#7KIl4&Cw#_=LBK?;jkOA7Q3Bqi-&}6kPY>}!jB}!s<181-mHz9*qLEVG}qAVma zVIg5Es>uf3evHjj>v5}`qhd;DXetPqa=imt5A86r4FLhMUvWUS9Mmc>Zn(RQ}_-E3)QQdZkNV#RlVv|wG|0znompBsg`jhsBwnF_LzX_~Ne`+f=wP_6N zl%K`ri=<p5YjsDiEW5pzJ;nu(daRDU#MUo&9RB0(1%f?I>W~(QhM+DbZI3rA>c4H}`eoJ<2faRCN;DNn;+$IyfN~?|c ziilL|^0;Y!$i1G{Xwa)?Fj%j(-mpB>M&?;z8a1qPw{aoBN2INgW08Y`mva|Ipn<~x zfpQ%kHdlD_sVRvNWf5hq#pX&wdWjWARy9GT{T@x0s;TadYnlbb>HuDCebLKO;zn)~ zX54!Grnk0Y{@&w~BoL&vQfK>!u!Ue!e$409Et~2;j}GB4GbZ=rVH91A z(MvPp7MSUG%?sRK_jiQsa5~;NI&6EPV?hmwrV_2l4+B8;C7SD)p^BLz{Q&VRA_Pck zP>Rkht9=0sn=MOx!a}{dAZjQyS%P(=dAWUNVhY|JM(S;51@4|s@vGFL0hVM?a%wzW zTIFlYgmSD$^E6t^;URA--d!3SS0$l|WJAqu)xgNMy7OQ2mM1AF+=1rul3eF3B<8iZ{zdk!m6!*N@@J)KL zStT-(NgQ*bNa#jCM&H31YP*AMulnvfe|@&QY5w8vAfgpyvZ!* zp4TgAv*%B4L--f80jtpJn@BUYn^p4na_k9koup>J`^*MNfpvV%IF>%BT=(q~5e(F~2%{Ubc# ziLv%6ftjRjrX1JK=Ty2dV$5v8^Y%Q~c8vJOc>_J!d;aXjIy521RJ6{OG!R`jdTZ34 z3A|WfMFUsHpAwN@Pg5)(K6LINDK32}QG})HvBIZ5`oEr`L0$=EO|HK@>0}NkanOJ7|_*fteyCM2Dv* z-8f?*<=i%K=5XnSJ{I#}7BG41&P|PiUV<@oy=BL=j-~BZZ}cN1%6esF=a%Mtj1kua1Wh>~J4ecZFSZ+f zkSkLkb@l%Svn3j*y^mo$<>M@sW-f6cxXdz0NEFdaf)1UZ$QGI;`ltmskpy8ep-Nz} z%w3mI@qf~;b4MtVWST_ikt76-84tJGClXm~;xXM;0MhNrY30lIU8(D<+8?k-`Cgo3wsB2! zQnj(!)wuSxY=XXNu%Uu(PAhl?H!k%MVLLSMRv-VnAWG0+T`3?t;(vFa?L9Boe;MjO z-&1?w@xKlc*BS7`u;lyQ ztKUUfvLT+m4PDMc+b#m+Iw%B$^bI1zaU9tRG)Qel>ss!0gU`-ECTNEy zYGI*y+uHRQOb9W$H<@=RtRvCGygq)JC%9JLt@x)uhv!;NX(5(1+N3ZC^Ds z28arf3wbRcZ5Zdf;OjZnEg@lx z|F>%p5}Rn56umioc?LNC@)U(s2KlUrYp%LnyZmX6C5<(Fmu+4E*UJoj$%<@z7nQ$g9 z9x)h9&R?H-y?+=edPUxxzdp0fkjGcv2i5PsG6T_@?SOGh) zw~EG07VhqB<4Idr%MloKm``Y7Qe)KI(fQ06ZP?p|wIjbHGb-|gx09~Ag^d<-^(}=B zn_0(0);Ke7x{Yz0NICQ1*t@AFWkHmL49F&Ei}QB23v@6M!MnxQ7?|AEkri|-q(5nUT?njo7|mnCagWM7Ws1~KJHiu zvKazVU8h6lu>pw|_>xFORUvn#?a$bG!C%%92{<_z2_p6BVKawqT^z3Kj!Gnx2^XPB z6r{MjFz)%pHRRi?wwerl))r$7YK{jp{zRYz@H%uc7K#Q*gaz<<>&0ci@Z`$$Ow_TX zBM=B-Sx{HJqC3|({^vaTndU|F1%XxRC1Ph$~4ElQC?^$mOdW!`KNJv;< zGA=k`Hju2YHju1x8%WlJBw2S?qQ`l3b$D0Q(!b0d3>+T>nGyk1$htk{+;#|xc|?Py zrC69DGM$n@i>q~H>OB8-;H*@HLZqAS2}j_OOq??i7wC!#mEb7-t_G|-hm?)t2$wxj zP$$8wIyu!Y7Wg(8?b6w2yTB&(B#_;CTIZWlyqx!cRocO#-`WXLUz)he0$ z&CFzsjtz2KNR9Sa5UMc?Gbd6T&IKK}1q!qlFY~}^SJ~KRLpNz)r-@3;q=C;d@wQ6@ zA!-dXfFj5Zb&HE5Zrqk;eT3h3S7`WFRpo`pAHYPW^s2l~G zVLx{u0sHGiK|B@!HvhJ(U@$04k0gcZs&B@e8k;D3AQHYc|5}$tG(62KO z7|`uV+qeRd4^A^zK7KQeOK_S(DXl?>6-LI4Y67_7#Om{HvxvDn&dhm?{yrxRf}9{34f@$U6+nBwPQ|vcBZ@Vo#2sbaylPpJ*Ei$K3`YvRQPaB+4v-S-auCmG&TJi*_u7w8`1VHnYJM?4yt9<`K?h98i5|pHRw0Z*+p`8)=73;=+{99-v=7-Cy`!ef*aSVkqOWo(Qmx_^-Xa z-Q9Bh*OO;M{MWs-2Oj^GA>!I2z|N0y!bvd(%sr}&0!xQvPHrI%%sm?-!G=h%Arj2B zArkD*7YXJ_Xzy6CL0?&HgTAuijX_^I=qm?(<)E(|^p%6Ya?n=}`pQ40z7iqDS7Ma8 zi7+=~0XGZna*OkKR)mxqr|UTpz;;f#xW>XKat%4!>l2~Z$5{`xN{7{UYk9HSd|lE|yG+^ia*I6I z^hrA>(a=qceA)Dkk8Ik}-wLy9mtU)`mvbcQka4T6&jr7R!S$tTS-U@3x!P8do~zB< z716b33A@_*$lU(hTb9ker0^OzebK}>(h1!Hq3%JNueMvt_tm!2(zfUTs_i!G`#JOk z)#j%;sJ7l~>yu%^wQBYVD$S&`0#=YQM6;ycQx#-tklFIbpz_Y)Tp#k&FOP(4{`n+a z>(a?o+o~w}IrKKwwiZoJwRtnhruE_by7fPGU-aYSR$?Z)v_?&xbVVj*ty8m9ZL4DE z`n6Bh_CWMe)z*9Sn{`&z_Pb$n??LxfZ7XZTs;xJRdrjzU-s8@+X()V8yR8X%fVy%y zxz_jg>NVj$)XJAm`Gr)C^17DRH@zn0Q{^P=sNU_-(E{IetXJ16wfHHR=6g_k_$=VO zCk(==c-ja%BvM%&W}`&qqg3irB&|t#)Sb(xLrLVR81hvH`6__=Dt-DXdi<3;Ue`&t z5~u&2=6R3xxx4OLDYLpF#*40XDO^@{Y-pWRU&YG$vp9Eb^1Ufa)}kEoUY<@RNZ&R3 ze9DeK3J$-#(_SKZp9T7SqW2obZ4VLKrE!NiqI!Z+CBZZ;1c9kM_Xh|C$hS?a_d*IS#*e9i5qu452KB z2G+h;8zbVL0LQG<1FE{71pA%t)UA#`mBA^h`&5H^$KtPRnHe$jCQ5Zyt;QA{ z&A6ayzF0@0O{jqM&IsLIu6=zyC12-Y+XF1oTO=?G`64SuhUQ!g{5ZQq^&X-xgE^-G zIpPUZU!q5Q`kY&Ad)(QzKFcu;F3oMMJFumZ=Ggo+CD(dX7;59CT89p}eZmC|WIOw> zDfV(=#ccUqqyJmJje2`Oj{5q>uHqMaSe|q)z#&{Ryr6+~BXzOCHgLkAz#Lx@bcMxI zQ!ITVygE%!jRQSKhiFb`S{yQ3Er=q;vzZ`L(klXdXP5+*S2JH}isBSge*U;AOBWFfnqwA5dL$*h8lw}2h?E3Ml>thc@##M+i{{n4)nS^#e}ZUfDj?G&iZr`Wm|u~! z)J->?idWBOZqd?QKx~%H(&O`+(Pc6rf)PdJh%z2@$~P8R|7BI8yVaMOLqONVHQ*BG3(kI6^Aw_qx_M`Y3hw_IbGvsq=R7Uf@|o zuGW3!y#c6*YCeAfPQ{vCgPO5W6LAiDb&ir)DnanV;Lq57ms7+|U(J>+3%A6hJc{Y>D7){UIQ2_o1s!f=azORCI@p^tm-P^i!L<;I5AMt6U!` z|Ah_P4U!xjzU_cBW)DU-gN*2>m0EEWBoS5wVHmw%5Y73VNMbg}WoiJZLVJs0r_)o30LA!XK|+cZiI#prc67$LaKu)4mP zAK(mFeDDuP>feiQMw4)I+UJ|nQP4P!wD4psASb6D3B7=#O1Wn~QP`e%!23($9ZG82g0k0y&D7jK-paHU z0_G4D4pZLg4oquZNexclTRoRC+k{ony7#1&if%FIlW9)33tA4q9&a`=H;y?$=jX3e zEJ}&8Yuzb|1=ljOcNDZ~_e*$JIWm`|Z&-h?6Ei0` zY*&L8+k?2d9jcqb_a?tsb=}}SK|b*N{vlH8ec{-I`i||L(P!F^b)k zz3{L$&aA zSnj3~o*BH9b1u{s$~S{rC>fhK)?O^-*k#_x++eo5ON~fZ@>PRLf2eeZXpM&k3hf+DvO-9;Vks(DoUSWVviK*6TTLtATMF8= zz6p3wrcZVi{-bHWQS)A4C; zfyU*1TqIH%Mnw~1Ys-u1f-1u_?L*w$t{(+Fnd;d%^H`?Q0k^-*jpfbOK`nwi9rYZsAvi^nzJbCCOyu&iY7!UcVq=m zSU3`VLcw^d`C>2&qhL;gOO4(3il;K=N_-Ryg?Z`Yh_5&eC7Mu(X}ZQD%sI9=QxQ=% zc{pDXiSSfGsKXS~h=gV#*0!6J&0gW|gtxu-)51ml4XDRBnA_!$Xo2IHbUTl5X7bWg z5kv$%vQ{)bx!->MpT}sj%#58HyNm(le@8@j1^d=FDMCjN3T`fG>$iY}Y80!aP{a~!`Y7au? zKN#zKw82CdDXCi&tG)Ci_Tnj@Gq`{o{bbvH@U z%DfT3Cc;3OCIXE30cTPP48eF-lb2NU(ABk-Ex<2KYShnZ0S3@_zRza#m3=;k+>T;K zasiK2qDOG4QO{jXBd9fW6EZCbFg8;zjIqz&c*ctQ7`{l&z?pm!Qfbuql=~4{V95lT zLS_@=J^%rgSsZ&Fk#~3Yo_e72A06TRjnq70;X6K|ohE?ZX6h8OVWdg{_Gx|&!(63k zL8Nb<352kzG?F^G(fPpGo7S~*KG(m`r9aB8ROhStWFg&ft)oEqi{MGo`I>R+ z@>;*nTifTZ?!2MB_A255j;LB5USmNRk^TihUYEbjiuf0k?{um8-4GDgkV+?E9r7z%2BG*I`nZ$lTo=nOvSVSKk=bf5|r=r^#Eo;RE z5`0m}XzdnA?|2`XSn*uO5WQ43BaT0e)wE+U&VK5#p54Rc6QJ_tfJ}D424{tTsRztLYS_D*Cgk_x3<~hH1*%JGn zGn6yo$27Pkp-I=1@mBpI_EUKvK)1I(H$w4~@0f>YYVngpw!#<3Hvx(Jxkapax(DI| zt%rsNtwMWx>x=m3))qO2<$NxPob$+Q=&R@wzA1N9xXfcB0>TuYfpR*cS0tZivWDrK zkceMf5?$gLjnk>upcuJPDRYFR!on|`8bZe=r!07^Kw4SnW?o%UJ)q}1Ms=WHg=~8v zi6g!wA==$(pQ+z)URRc&I5i-|Fh9dZXL;2;);bhCb}Q}`T9M@y-S^R1^u6XXWbr-u z-uWHTN;w|UDtR8!I!^6mQ_Qiaiwc{m)%E{k2lyjN0?xw1lf9ij9*z(c_G0H>VR!6$I4M++ zkP1lB@8V_6I24{4?rhKJJ3BNm zjT$Al#{c%Ojx#muz5B1WmVyY)?(>GSQuVE`InA{G%w1op&exgr&cr%Tz_%~|+ggXr zG+#2?qb3>77r{d~txV7eJ72M&3B7zS*ycK7HFI61nwV)_TCSGLV)++ZDtYB!>#m{R zU281=o=?TG8n~XLZLdCNniuoXR*B5C2T@A={wkE2wo09{{Ny``PIJw&nx*Mcxvblk z=~cfhKa-Aa>~ePQv&r{R*UYqARyXUpJS~;ZR$HUbQT5C;zuIR#)~BluTK@(595vBQ zTT5j$(>x^NZW~Zntu*&m_s!=`(z~js<$<+xaGjOZOzVRg>#wS2+5@SrmS5YBjcTs5 zX4-?e^6#PknrSOnVarebHsV8@U^U7_i<+k%b>OPUtiHRZj%vH8{Af6noVH#nF7S-x ze@Q0k#dCmu`Lywup{>7NCJVeBh){0pzs8jRs2M&*7lbEDj+b~5-MWvRot>R$PoMt9 zc;oJr{{Qspll`5)>^^<=Z2$S*^QU`H{<5?CY;X7ZU(n84X4ugZsj&FV&aL|@Pwscp zMx)UNj_D6XNFcH}rsPHuCPC=8_2#d3H$a9*M~PH?@y;I0mjslPl(UU|g?OlYZ?p!6 zWLQmHM8s%D*!VJ;kV!(LkO%O?>-&xY*--y6%f&4=Z32;4`SQ_az9(n+4hGO znQ@U`LL+z${xs9j^!nUDhF>Ep)wks*uc`IyjiW^1s5l-&p(vXr5f+6<8wkmOYjT&e z-r)v9c9t-rQQ^X*Oc8O!qh!I-@%|(^JH={#fW~^l*Il2%>Dt571nxSRQO|&!}=|GvJFw!l+tq z!!e_iL~$XvLvlr;ZAoV%EP^>zB+&BzHje2CCc=zXZxMcxUa~dUc!fc$s`A>m2ef&h znKYnmda`sETy~~+$7knxT_u$+#X_dCY}y~anv_k!DnzXTM#FUWWRtceqJ$~UR4k~< zA{&t6Ww1v;zkn%>LtMi?pD=WU7bH5u)p)5p-Ybw1M^zTJVYl*Bx%_g433QaTVQbG~ z61eh$PWDO9kpA)8WP$Vavq2haRv6cNIaj@zTM7=FU4|0F#e{-2yzn2e#IT$GeO4FF zQkU6%UzhlOR0dqq&t$1|Ap{yULq_zy#?+J*#0;Guo#qn-E3OYVkqS>DD(77pS==TV zszoK*w{}b;K3Y0qGu-XmTF%xa!+ZH&DHW%|?bSFK^y>WlG)0wmVLZ>&)Hh#0iUq#O z_U)yMlW+ZGm8z9BojBSA$Y8R`TIY*OqUZ@hp}?AH)jy5cZb_m?7(f6 zjngz+X0)rwgbn`o>wifU(J3+64ZsUC#vV3q|24nUHMQ)8NgW_P2qRi0uCuNaaGfcU zMp)W9`dCd@BrSy$Bp~!kWAV8?Q^F@v7Ze0GskU533Ue?fOO={xTu;D_n{JF>1C_8r zVv+wo!rAAY{pZ2YA%Qz#xw)K7i~4!QMM}K4ia2A(q7mft9#6YJcZ)--B9CTGMTSEq z-0AE+k6U%PlgnBSiUyM$)(ch2-$Qnx&H;V*F5kIk9wjOCzFw-Hv3R@toxWn;7-LB2 zNf?nkLYlL32ace3pbq#Lr!-zK8o8m)p^b5a0bUM$eaL*a$WL~5{WhS_ z;+K}(C_F-h(_j$F!?a3vE;9t&F*TV>B7#jG?2Lhn2)+at(F$w6+nOHRV`u*uPFq$69Q?IPuMPfM4edb~ zZwG&^!C!0e*Bbn_27fKr27fIy_-i$-?clF9_-nNsvlc<6dxv@mCuv$*Wvp=w5kTRF zaHRA6*MZCNLJZ%gd%}^)ukUQXT%ao|RDz>6dBTaol*JLQJIw7ePqVahYdNQxhPTKz zol;G=Jku@7_{%W9(u^qjfnVbI%m*PEe?JM)^9^7*6!@9(W&O+(6aq6IH6*{sCRk@-gFsAKKuDHu#|perSUq+Te#a_@NDcXf18l83dtHaMd-v`<*t(|Ml+p@a3E1@glsNKHBqtJ$t_YtepRAcYpWUkpJsG+WUWb|KYtx zf>Fe0{Db{F5z&W@jYIT`L<U(*Pyk^EBysE4}2<$BmC8-OPX8{|bM>zcxOiS;Qwe8q)=y zk+C2%DwWXiZI-5w=wdVE!KKO2W=6Gnk?T191kBq}WYFS7gOjMzfx@!_Y(^su?0lY; zVsHghQdsb%+_E}Esw9b~?rpFRjgK^5jOR2~rlbSw4H6q=qzL<2k zf1eXj3@C2Cu%%FdeaoaOQ~pDb0RS4U0(3)}R5*%`L@2*w-^Vk7Loz1JSbdtiv!5YG zuF=uEml93LlxvCJKtPY^6k)bR*96j6DZ#@ea7+zIeB|2YRMYH`UV+EW1L|CTz4HyL zuN2nHVx%L>7okJ52S_B0X&gFw_tNzY2X2hcA@!F&qnT};#DWB1$)OBkr^OH?B3P0w z6vO{Pt1)gwn~jf%aZLtaaegUtOFYGqEN`h(KI^5PYtlJ4A8mZp+`Ym=ea8&sqd8`? z4g|5Exb%r5Bk4_+D8NyW=!vr#!mu^LoCKFLS)^22LNCy>{TxX3n6N76U(NDX_!!Y?y0MESQq+7^hIJXrzd<*LVJL`{ zHPZ*76R0R<=t6_JKxRT^V1fW=pn*`p)QepW;B8udF?}JQeD3IUj80~Z3ulbQiQ+QA z#^NJUN+&C;O4&Pt!{wRrsmglY(Y?N*4BSJq;*Zw%DqrDCAH!&g0zrV_#5~NEF%}v) zyiJ{PI^a;)O%FA#>^rWG%+$No$M7)As#_xv&9zlWQ_WlG6&D)u6pqj-57A);$FT@j(lq+F_iu<2G>|q)WX8)Cn60#i zq@**sy)d7QK)e)>HIkalk%?49w9f@FsEo6F?y6f@3_3d1h#@VP!LK`D;+rbW{S+p? z$-~nhfp)$Xq~SD$(LaFY@c1Uyx2E1K`0$b}HxD*( z9FL3m1IJ-&lgD~%_4k`6Y;$YVWDeduc)$6}FNm@rNL}T>n)8>ay zTg|Y6qh>)T{P8=(LXSbaU4gp#@J0nzck{!i5Blixm`8lJ{1)IBM?B|J!HRs^+B9|c zHV<}pc0PUhWNs_Bw-6W#%&bTv(sdfFGyzsnX76Z;Fj8a@M_7?9BzdZP&zvSWoB-ha zudtvR6D87jzL~GAx!&E}&SK3X9C0RBnzivSDO<^&^vaI<#)RLY~|^_?aQN5wJe8br}9#Y_w<5 z67ee{1Pw`%!7t#no$r^%rq$e=GCH#y0v?$=|Kt>~(5)N1*W6w64mNSjo3Y>vy-*1d zE6r7G9&GMEd$F^*ReB}^fq_HOE4*1@wG~HNSmexmaFosTebRPkpID{Pw=X0*F|U_A zz6=^k^tbO%z*cp5deYpJ{r^#frF`44!6&(Q(tP~}owFhlMuC$a`l2UVWB2YVXz3PZ z?=(Sa+)LP!HyE6M?92H=7qx`YfQ&1;fbGrvp!D;O&(A9ueBxFIx<9k#WTU#l!5n~s zT7;$jP?;LzG(OGo6@i$Pd_Wp}rq96`J*w|w`}hV2YWq}>DZP2zJZyV7ij-_WPh!DP z4c3Mr)&sKMMF!^p)ilce?fa84I=iOICy0fR;x+@9f(}Az`Za=C)RfEtinze0GbHbv7VBV>0RZ6It^J3I@HgnO4?p? z9DZ$TSc^vLW)wA}Y!aT`MLFOnryUgB)jvKtwJHw~1}94tL_7&y#F#Ma}!PGeRdRSP~=5heqv*8NxxJ&kuO^uQIfDnsS)BKji62iQ=0L zLmD#DpwnIT96~))v0r5>8aui*Q&B&#xJ(~b9FuQ%f-4^IlIC|NVnaGl-Q&VJ$@T1} zTTgSGb!sdxY5a!B`H?08V3}%_nDy(1TsWfOG}z#cS*t&R2nRbYYaKb~mxQ&Gw$?B8 zeg^Tla6(K_5)8UdOY0QyMYboR3#ufDwq(JD7TfmrpS_`4N^^x*^wS_tG(WVnv)J6K zy!xq$AuO;1`UuzP;pvnF%K$V7<@0vRLzF$2CYnH^N7jx3HpnnErj=NbGsR;t<7^p1 zl}P>Q@b72n@b%YPx_kTj>zZp&5wWn|?|&0GAg2%$QPD3y*(s{Eonl#W6W?UbbW^V9 zdP$?mAxO-_F?xe936ffX($^;xt1*sT)D)t}VOW|(od_Dqv61bfv9TIen72)Pk$_1l zwFY6T$y6lAflyoU$0b>!_fUET>mSX6G2Yx?fX}iXQ^&^-unGF8k^oMGu?Mq})5O{` zI=1{w>x>FLc+Sa=9)7&WIkzx6V<;fj7s7jdk1RYJJYm$z5D5l-10NO(5|Gen5w3{g zG&Iq%mt>ggqPusl4jBsz@^!+(sBSU7#*#dHiU*w4zg|`T0f!69(xOz- znbAFlnwr*p)-3PWt|dFoBHNJ{T(mWT0P?PJ5!h0r+b7C$uK!ElAI8TNa5UqLa|Y%G zW{D=x4TPmUatpvqfvkJcgSS0V2C6mRj)+h-i*s&a<`G+Q#HUjtJRx6_WeZ^a)W|Z; z;50jN1UT1kCri6nPqhroR3TeHJaUuP$oKNhF*tT=_lXv{MYv0($ahnZ5=VzxE$EcK zloj`zs07Q&&9zr>Q8;mKHtwr>xPru7QpL-jX;x`a01029{1^2w z8(h%}BSQ+$Y9lgCX-OC88St2r(C9I&{}4d0Zj5t+vfBV&RllHPcICX-x9s80{{1ak zx__Wgxq&*zB)e-^qP$B_s<8)dRS%&sn6STxHK6vD33x3|!R0bBc~d1S)fFTN$q z^d(K}3h&sZvAUc(-=iO}=#YVHXJe11_3MhUbkP(UOJrS(>V1liK)dh;$F+Wdu4|)% zKnwkv;>g61Q&!i{mf1sY#S=tXM43g6Cda0;1~f2rRNcNqucl6``$-TSe#fGEe8`Po zW~k9I_6LU=*3u)#S2&^}R$Pp;u#k62MC#m$a#I(HwNl|Y*0Lfq2Reo3Oi5$(ea>AC ziV#B{E+}i9nxPod@{;AxYA9_wT7J!lc;^g&YZ)?t%o!EeI`BD{>Mc>%TwK=OL1$?Q z8=YD;gmW6oIRQfryz(=FQ=$<`^6(ZstmAn!*9&HCb5oY|!B3w)v0lac_!wVCd*PX2 zCaZN#q2$a2WAu(_NsF-1h3zOKm@Fn%^fpHWR*D+3L0?fG;Sd2 zz>`N2y(H)eM>OCXzLx!uzdd_gGuaqK{6tW-)K@9FQ68}S!Ws>^=C=o2|J9U z)JwY7sWxSYl+_OBXuOuGoRdz*3mP96eKHhfXl6P@JWff!A*e*ja=a^)kWfqYub>^!5Zin>B13ceaGIFS+G+ zfP=Y79@YZbfJ5%h%-fRDF(c|F6(!RYQNeb==(au`iQHyHjYBK_8Z2-FgaA}3zyQBY zVNNKk!o0#_JE9YSJE5!sZbDfND19AQB_qT8*~b+~#q4vLsgKbvr(gZGpTn6dzfdQC|MsCl-hgBhP5M1NB^d z3a5d2&%V?zj69-5GLW<^inIzaSx;K#ardr+7?KDt&-C?}#PzPkRDCIpfzYa(DMQNE zAxBEk*$gt_$6QJ}i3k#q3xYH2+n!RCo0td+w%MqJe=xe^Nj%*=(4^i(>1-ZUqj|PA zvt`~q*jx}X5j30;@Uon39&GN6UyS#y9$qtT5Iger1U<_4{#lQ8yZY15 zVpmU{2!G!}PIc3`MSN2&xRpbj>Y#nLsHWPf{cZ!AO0Jtdoh8p|-M`B)S9DlBsi8yi zGdm@g$FT-CKQ~R8^X@BmQ}meA_d!k6LeA$m?;%%)m2(TMELstuyoDQ}pKo=GM&eN} zl7EV?xIh<01;<74lDMt*!@*gznkRMmcAA>>QtPSiX#{kqL3?eT+-9!{+?p-3YdL|L3r88yR-N^aNW(%=gz|KatG7H`eZdF`_G=gC@V{H`V^xp(J%E- z?bJyNpTj6sH;#Mqj>&U`pUFj=tfWsk_E#s6HER>{4)1xqCikOJAS)^qj{E8p$O`HN z(o35_{t)tK%`9#qe%3)-S^BI2_SuBbx{3X6WlvI4gSFU0_VebD=I`n(8=wMf{?^(= z>MSs=eYV(AjC!v8=_(R-J|&$awpEC$#5x~mw?o^y^>EmvtX)*}YpG%k~_bE#suPA!m6y9*7K0Dwpx+kf5uhxF`9Ue0K3!AJjla9=;fXic&8F`|o-OgCfY5WXMG* zw~j*5G$lWkR!^+lRN$*R$Ez~H`n8!*O}pr}eY+8GX^rxrE3Q?$vnh;ICw=3#LSvOO8rDI6 z+ljdZDS?@cvCzDlaq6!hjN?U5PLCm6uId4Q^7icf@a@s@k5;6ATMXTD(Q`OTc^?-P ztcP#c%h(Z5_1z3NJ)c@(rG8YCcsiBUq}JPVDYvI`dlDO0e0ak|mwkQu4IC2}agoIj z!R<_%dJ3S#UUOIGIrR35OZBBh=9fK_yQ&_j3JxKy{K>T9&R$tUW+R0tQ$)b~X7$dG zveTVS#-yu<_@ga!O}>mGq%TXyh+stnIOriUo=XH#g{Ui#RzF(TjY(rvvlMC(ZyR}{ zX5lscpcP}cBVCsV^TffXTTNdaq{>|Z!XXoF&JO1(XyR-AZy*obp@$~TQ?C;;bd$~DA$EN((K_SxW zz?3o5Ko4w*B^OfcW;k+x{4=tZl_1DC`*oB~%rpp4ZW~N-0aYYx^3&Uz2(XTCxCzCA z827Z6?id}GtQ!ama)8oLx6mfUy)WN#miyNc&i?);iK5Nwa4dwgv)5;DPdtk^=r_&1 zXE*&q%1APLVZ(jL)ovQI%oKvQO*~R5nTfu_Oy-6P)Q02`elQ{DKs8s&N2n@uWWq+B z0^j(9g7!{sY#h3HG$<5;g~@tGLUcv3G5cHuB9s>&9_jx*&LX$QU;4K(Y`D3vF%K3LJ1w^cK7aUYSG-6!IWRa}R!aKPS;*6o~M{7{>pSL_|?G+t@gi zD8?pP!h8MUmlA0$ljA6ig-{P@eZ{x2>r07-Jz z%Z-f_rb%>|0KtJPi%NvUD|_ZM6KFB5YTlfy@3dERHh0vDMpP}w`EJZLsODtjBD18P z^3c6PM6NV~Rg4G{CM?7(Si-rm*2XzWO^_38%>qZK-=ie6#FZ_w8*nr)Ur1Cmv3__D z5qqT{t%=6f_2307tUF{^B;v6?g&P}M=!Ub9D~k*qlF_12W)2L<GE6 z+s3b>WNyE3^Od&XCsx3`<|;d7DQCtT8(A^91LSP`mON~4r@N*4k5Be?pAYpP@1uQzjtp5>9GTrWs$@7n8YMpW0Ec#`Ej#~rbN2t@ zasKz8?3d2}-jn^I{_DN8FVHDgnvcsv5M4*T=GTs=!Jp1uXaX~h| zKu3JB;0*n6bf#rbxiOwmwGIC@!`~QBeiqyCU-!d&wypo;{wc56b_QsIgUci~O8t$m z#_~Ge_-Z`Cmm6P=)k6Qx1)Xht_5W;qfquXOH1Q`dkLAWV7946)k7-Eows~FfpEkx< gGTRV|9}Aiy8r?T00c|9UjP6A literal 0 HcmV?d00001 diff --git a/assets/new-relic/nri-bundle-5.0.91.tgz b/assets/new-relic/nri-bundle-5.0.91.tgz new file mode 100644 index 0000000000000000000000000000000000000000..e2117c405cf8a04138d4d69c4866be29ae58d080 GIT binary patch literal 347008 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%0A&k?Ni-NNSswjjF6L=Ke!rl$ z-m5jvAeG)9Y3S=18yKcnLJpSrY?eL<2vyX=x~uP#dZ~nnG;>3uhDIyGuH4^Q>cG~V zpO?xm0q&4m1E5%Dk{F~BdtKFpnQh#mA!%n)`s%UuMYGa+9hGx&3F^lBdVXZ;FvTab zF4HFc!+74?i0gHXuGjS2J+i3k+Z}D%F^{z4#{Dz2M!*c*(v`wKN0Ng@TK4FsVA1k( zwg|#y+K&}KldMRVA35dT62X>wr!v1lz)X;n)Y?&3VJWIH%>`nF?~>P?nDXbRy+Cj8 zd;;nzR&oBIz4puj<)j;a$ok-u~$lKOfcdQU8*Q6$_V^>P?PdX3e=%pK; z{Ll+R?r;aGvUh!Rc2Suk2f5xLw$;(;)mPLtIp0xsIVLPPr7t)PmFW^6KF2twN#&3Y z9O$`gMfPpu5@%cERbOE?dtGP()p>y7KV#+7@(VKRFj#!zE2nGI0jK!b>)eD4n1_+- z$<3+IUQtze`xnr@{5bebW0i4<`@(7~>Qy?*GgJ;~25Zlq_#d(;jQsXfH@wy)?~>o& zIYGyZI-@rkBG>yj(Ejf>5CpdU<~e_pKevK$UI?L4ohPy67=hwP`}%sFq4iMtD4;C% z{=S_JaFBcz)N@*M*;df6px&TNs;NM27wRhKa7R4ER7xcE?ZPx#xRp_j_&dXmY|nIV z?JM*^3&GU+HsLs}@(P?iEtt3~d;cLWob$4griUpFQru(*DcOy*_A=F#xeFqaO3Ddg zT~q;}3yCW(*FWza)x6p3txDq+f9(<3l`?E=Yez zxY7}coS2sqxhf^=ka0gl&^|2F$gby$zl+waJupeehYo)JTw2VU5uu^~E<)G$5Ot5d z&Zf;kf6G>J`LGg7nb!eP{p!cXwlG&rx)*>pYx`BK4kC?7SvmIKt?4Rd-Ka=G4Er$l zO_=KY-=P#xQC(7D{8K4d%*$lb=5^JL2URg}nra=0{UrIWm@JxPaA{f9WWk}uidn}* zb-r8H1%~gA?eyGpn}6UKKb|0Hk92Ox!M>mW=gnN30(dj^Y*{HKH93I^fZz^G9@v^H zZ;lS_d~=OFXpW8G|763PS-YQCVYh8GH1uq4=*?VTv(5!@ckMT8^+MC)Z=-o4jG|K! zoM&5uUzP$pO=sUizy=>s!ZJbdKwomMIw5!tmb1y^n<-lRz1Dx- z`QS2A&|~EAIgN=1K^zk?8>h=TpXu2jR5CsN_b17-g80iyNJLuYQnaJ5PAJLe9*{iT zkWy}ZEnPYfA1|i;Y}dQwLmOX&Nycel`tcX6y>~oL#iPO$IW6ac_fioxw$l)C5{;`c z^A3%>?Y7*(thd-rMni43z?^Ia)Wy+1(J`%gPzm)q7i_hMz}pgt8y8#xCak^}k@}=F z1*Dfb^S9hpLzHd%I(u66;S0P;(*@KR*u46f=)GR$)Q=K3ju@v98r{ZFuT5FWvVIzY zU14GxLgT6IV`q@c0IR52T`kp(^)UfEp5@Sz@U7bqW69{uLIa&`_5Edy-kQJmpij-I zNRTTP{#7TN-SF{=(w6W(+?gG@rglz_T1*eT9?dfga72^inVRpFkR$cDCVQ!7dR_yo zjjRc%84>J^P6J)1v5d7AQ3fCnt;xk^nXuHd=P8?Z7HyH1;tFL z*$dV)N-cS%vaee;(8VAj8aglo1n#QEjOWoYcmAo9cd(`5_~qsN1vAuHU5o|)g@)by1I7T2BP^U=_l)Xn- zBX5BNcP31-fQL)I7(r^Ka0(xm*gPj+&8?l&Zuu^TY7JPh)BeNKrg&vdXw_7k@cmLi zqDL*qF_-qC!x>m6n-cySqNfskzQTl58iZ@&z%j-mdqA2A_Q;dRRQrVmOk)kpWCnoI zHe5iE%qrz6Lmfj}dZ2?up)O_!TeQ^H|IFUb2k_DQa~>Bpgps)f=XXL}QM#&Fe8DMM_!C z^9^=mj-^I(%YJm5pqn~dQ*)ZstbjkA9Cl|pQt(qKoOM{I4~{Q-RvY6%KJaME?VvvO zRcc3>Jz-TDra{A5En(L>ma&*UnU=>ZOIjyRblhO&pRT||NG12K(uc**M!Hg(3000d zN*MN?FDF<_dhU%@^;R((5slpzb6sv64qVnWog5ij91aTBG%WO|uUBf;w3w9YX%nTs z(qly!7jHRP+3m_Ol((5ZPTyWMUaE;$f$j`5Wv2>e|Ge-tWg(?qtj#F^a@8oh>*c`# zL}WE&$DNne-PkNrz!KCR=fN`_;UIlk+f|IC;WFIYaDDw4BJ@OS3C2%Y7m{_6u5R(3 z%PKi_RimZ3d!~Ego(rzz$VF4vtjh7n9dAkbO4{Igx7@zQYRo_Do?bvjC8OkP6@IuE z;wkgNSxq?_lk+SGw+>TwO}o|Mohr>m2;sscSL9!hM%tDH97G&+80Y98)JDS+K&S#W zv_o#AK4MMqZw$TxMGmhpou&T}s+c1DufQ-x@l-=VDt@O|i0NTyocdo1d4Dl4WkMb} zxi3?v4=O}zp}!l3T%R4-3Hl6`wQZ6SX^z5B-aCZw^GS2r?YFGE|+_) zXS=4Ojhwud4x|tTzd8(48hQ$PkiUcBzVDYyj~N?DGV59K%3<6S=1Y` z3ti@>GE)m(^JlCw#)V@`kI0;yX@r-fz17e2s z2n?~YydK`TE^$$O#$Vrmv;>)@<$^bfyT$sLx&BmkN}{CN#0lfCe+X>%+;>FRnvvN2 z0)J&OF4H?8=dZj0vzHwFWL7-N;n*3mXK((tm(yvWLUe6tx@vr_gl42j+ehPdm@-Uc zTpp4kACK?)G-&y{ApDFNomM8glYpF6E^B1oR9+WWD~Qa~rku7dUchIZ$$tJ)bv&yL z7Dv3VU6oAY>0=>p`*NFEN*;q!3eiWkeK`x2vLrr7ro0gyXhKo>OHcNsqXjq4JDJPV zcd2T>R!EHp`Iz8TFozK0Nw12&e zKn6sVLQkLF;*)m$4x%=FKU}_Jhvv${6W23HgHdwmP%>g#n@go^wttUBgNmA_n!*>_ z>Eld6*A9iWiYFb#@Ta%`!k$|vJID9!SbO!~z~(Lj&AL`3gw~ne(SoF(R*LF- zmX_w3T@*m5`cKf)JgZCjW}~cmT$S|8O66~OY}2YcZlpcV7WVgJS-LI1b$@QTt=pdP zH+ieo0HKwNU{UZ!=|;zmnDZ;PO7#>jcw>fRnM zBCg~^1$%?W8t8(bZApFiiH72MYXI}DFnrx`nou!;x%Y3A2;=nZlmRYrkXwd`Z@3C$ zi%)C?V+8cm4!A2Z4IxBGGz2CzWpLLrj(r*9)3H5@>~V);R(~}O=NNnMpf+M{TC`U$ z0>^e$c1!*N)8c`x*#oC_ViSnr&i;lc0QAkyttavS4;@9Y20%wCC17(X%w766{|6oQ zGDqNPWz9!9v4Kh>5*PtyaLt#Z`g!C9&jT4;30aDEbJ5_*Tca~zdR)& zabU2)e33nWHCm(F@Sk6vEJOVLo?wlVC%bh@VHNkP{7+O1=db2b9QKR`9d|Ql^K&TP zgEBYKHz4n4ebQH((oN$^)1{GTYKx*eO^AvVO~`bzDzsKnv*Oz4f}}xHNy?xmm30W9 zT7%kZ`IaPM&^6h%*TQ*v?F8RB5!o7XswyeWj$mrT*2#JF{hHpge!4;j5RMm2i?rl7tepXM zHi7~4l4-Fy`3-NdtTccm`JZ%5Ql7d3-J)ahfMl(qIjNi#r(XrJY3=MsnI`~PbTyA{ zjus_-9VIE>t$;<>rU?+<00Ll52K`^N#T$y0AE0}$n=0{ZBVom?nA#`3E%OdQACr@L zd^kG8Y1aoO#aaNAsct75gHBdj?X=YDaf#K#LXGZqAM3%+IDsE5jA#~;!PA1NaJtq; z^e-FH;1%o!2@7W8YQT3A0yNbu9G83y0VekURiA*tysj>m0pn2C=&gP|Uu@Xd&25b- z9}j9Xv+g5tM-hKqv~(L-y(6m(&w)=P(;MOk5PEQ%#;eZUx5>xJSBt|g(Y9hlFA}rb z*9VlYhF}Pq6PgT`s~<>-!DVPZu&4puZx#G2WMtBhKqWbuJ&^sAmyLl&Ouct|_#beM zsvj6xJ76h(0xAmFBnSuTK4B?7SYb0wU&QSBR^Ac(%BTqRSUZai5Ha?o;Y8{uMVsV2Wt zQO?F-TN-AACDhNW_%h0?zsa#{-@W7~47r+*X;x7+I;p&2@XP=f*w(BMu}Tg+($pJV zrr}@{7(V{jyUYJ0ZNLM8oVaG9zN{E?*{3}3Y{Fw?+?Ryr=8IpZ%y?BTvd2VSWXh<= zd^?=WD&x{N?zn3tX=m5~B`st`BjKJYV`{td`R0rkZ{UcFcPjK<_1$?&9{lGSLxtob z%*=m$-~6bI>FE~v?UgetRma7bQ{1dW*fXF&7C-)*D97=N&%^XX-vP`26cA7<9dLNo zze*MvQyz&@I&e34V44h%+pivP6~ZDj7Tg`b7tE1dW^658Di;@Pdk?G>h*b*Mng0i? z3q8m2`5VHk-II>JL6dV%QoPk8UP^`L5ZAfA3^VEZm{C;0i*4yJU&PC6D$&?C;JXuh zvKhF#89D0YwEGRj4|9TMF!vv-?}R_2|}_a<8G9YwtoeeI5iI92^uUnVmz+7(f}i7kFXYmX(9k}cZA;; z#%87UxGJVyoOUxS)p|9H2Ag`)O=6K)$YrSVX&i>(IsQS9A(yYq+vo0TxJfN*a-Msx z-Cx!~3}|gPUeYvDrhUqV2~IQKlvJMoYv!68?%w^tjILpuuEA#lfTfl9Ja>N2lINuD z+UVV-?ht`Eob8c;Bx*U1NmtZ_B!eIHx zmC+s)kiGPRaNGAx@-X?nk3guh{t>~>_- zgfjW%s=Sj%-9VG%6=pVFX(&c2@|US7?(nZ7y{nfq&2mf%N%iH%kWN)xzp4ErR_1_i zpZEKHa`tdT*B0rjbvl4-A%L#{>nDL~y5uwwhyEY^xX0(qbV3sYw>Ef2L50&OjRPE8 zszrz^IH_R;Iy?4r=_(gDxsh1DhMtHXLw#`9aMcI6p$JOZW_d=mT*OS1G?-WKppPR?~M{s~zHE%gCBP*}o$`5};;h-(@s59`a|8HB+%aQ5Er zH*Gg)qajjhxaEPt?vb(rL}TfsJV!IDb2@Xy8F+`7uIJCUL2Sv_R&wK@= zbcE8qAA;}qWY-P=`V8;Ob?x&tsR!?SJw%{~`~7vB^K+LrG!zt+*Xwt;eNkoBcRS?9 zAHmZzpmziBYdGtx(U34S)aTdxG*^+{yM-KJcy>jrBi}QO_olJ?1YJ-u8geGFJX9(d zq44H9R!k__#Rs4js$C2u1b6hYgKr1ewvUC(9qTD(%a*+Hf6Se)XY;q9=!5EO!6~?u znXZx&J&Ztr32JxXHU(kMrK(^pQv{qQ6iwz>uZO&OccnVBo zO{xd7uVX#;;0Vs=@-_&yyI34e_;O~N3#9<> z8i{j<75~-9Yo|<{1wa1%H;`o~Iz!&(v)+IubjQDi(U1`ZDX70W8_fw%y(5MP1;sij zkweT3QP^+V=Cc78@lv$=83PlNAFTgfU=aS!M_{s&KlWtcnT1)swbd}xG~A@Xw-$Mv zKakPh>;A2bB!`_d5@Urw=qFB1C&&b-iTu5IiwLcoHLac)w#wG^*i^G8~j)It~L}2 zBlim9#NQMI_<6D<1Gav*XLHIY|J0FEk*`^)jSfl$AiUis8lLx+<-4q zjwA$`%p2u&q5ok2&$>e2VT@ARd1&ZPHI3%Ds}jatE&dvxE$E6P1dA7Nc{uH_1M}ku z8ctx_VT-ETy2FttAcd~Be}m8~4Mossj!m{nP}B4a7c(;aVd2|#eMu*pLOehlQQ9f? zZ8NF{Uo?23cBdk~o$+of)!I}s%ZjGph7{}lJH;;c&%_C-0pq^Kgdxo^g_2+xgrN-$ zcAz-pRttU9czPs0S`%qz~X=DyAt?cSsGY0FDTpJw%AJy(a$SmYXyx)dh`a^4YQLgAG zKbNs0C4Z6a2|%;W!yk&-FAa{UQ_d#y6qUuPr&8609!hqv) zjWi{u(2NDrA;c_q(Md7m7vh7yioS-hz7Bei{ve08uRvah2w3Erj-!q=5V%3H?<{H_ z4aQA?f4M}Y{-r!`s@hZ18ZX~6cnkV+`v8OoJB!4A!%VIVVGZIB%7NtNU61ib7CH+eX z{c?84wSS9Z>ZtBV!!Z}`Ah#X=X_H3<`3!Qg;`&LfW zh^A2ea^UokfxdMpCuSO#UPA z9anzT9mi5Q1eK={>L+HHbhg<3+o}IVSh)!w(C#YxbYm<;^$w z+9#s^&NMnnSMZ-!P{@0Y7YCA>W*Fey@IhcKi&FAUyMS#O9ot&9+Y%ydEu@4p(9+0P z3=>me#e2W@zvsFZ+C9bdzHf(j2Vrp^Yu>jZh=>0!?=M#`2xf`E+_cKC$L5_@ZF9L@ z?!D{ia}m`WSYhTqE;S2f08LQTjie?hQxc9c*E-?m~~OzHM|$Ixc)L_e@RK9W>>vEdHHell^dB&?N{DCgmXq z#s`{MkvPVO;lW<6uSp;{S3*0-2H~nhz0#&P>X@$6 zH&IewX9n^!CLQ>_AD$rerojGnq%Tugp36vSIB45()4`N@_U-#brCKkICWA?p{%--g zNHJY`=NFTy5`8m82;Sx*Y$2a^*u9gGqXJFEzr-A54Llk4lOuOD@#=C$r~G=z{Y*&d zw|vyw<75-(fbf?xHNUK9tx+|9^`GvieEGyca^}+_Q+h;KK^mA) z0Fhx#s7nx|y!K8ZO-v}APWKh*3lJk*1AzUj3O1B~4=_UP?i5PgmlL2W3d|>N& z^bz45V6K5%03QGtL+Wzb?>mLC^!13U{r@(ICRSU242|9ok!2W8tRNVq%?54T! z!c675w-RT#Q5A1mIq~P`AUZ&$cs3%w|E_eDLW2};@_zj6sxeDrhxYf)>>rI6i_(U2 zcMXcSz&!I5zz@{>{I)L5_%&{O7^lB^?=@R%yW@sCYe3r={2TIeCay!r z#jo{`6Op1Zq(C=2$ZZGU(k$3$c+9P}85uQ;_H#)v`Wx`k$bBsNDkg)3Pd+y6`%y}^ z7B>L}i(vC8*0)2>_`Q*Qi_t`7hCCfS3NH-iqS3FH@FR_&^=CE0sq)rPzrMOhX7e;s zY4kQmLPkUHL5i>}+?d4h`+G)0+*>Y8tKUB!mW=mmjD)o#{caVvd@PpM^`hKv4O=cT z4mRzES~p{C0>TUD6u5tF7cSP7aJ{5Q8nrzv%bBbiFxYR;oVHro^DS$-!Onbe;J?;z zUkmEkc6#Z0LmDr{MNNTo=9p8`6~)M;NhR9Tjiy;AhCdcLX`cSBr1YLDr|q!6=8(0$ zS3)Rj73#*e=ty>gZDg%wnew)BK@LdZ6qAQTJqm>Fi|-j*tNu(g(%CdH=)L0<6pSEi z*Khgy{4#niRN#%=j!R~#;*FYj(mmy5tSX#mG}?09D3^+w4E6DkS;2Br(fA88aH8@B zP{LC>mEWN+n@Urp&68OnO-zSa%hMM`0zF)AMQ^70SdrVC z8jv8TD~xIbx=}`{OzxXQKNf3VFpWsQ$~U>iB2eK@%a%>{E19fzo#W=ZR~M7pw56}haTwB`{pi4u(&F@ssvjkGS02Q9DXnk zaoyM;r^GcY5*=iy0uvq0E8cF-g7d?X}A0$iaDV_zTl zUCl<8%2{~otFDVGJu)>1Wpe?RLm@loWWI`*uc=`JEk+TWrOeDy5?-_Ha_4hP$+$Z6 zn}@h$uDZa}L5}7QufME1Lyv!zo*zC3L4nAAKQh@;e;g z9eW=MxiubsA8Dt{688GuUvmb0Y&Ka3V&?Y*`b6REDfPj$U#uY|##mw8)=h&6sn9l6 z#BrDR8a7u{tV@&jS|iZ6)Kwnkb5`RyBsfVPG$jgV$j^?aZ>(XULsTm0Cj=zvJSZ?V zf9*7{Yp9@>!%HQ%+|EkSD}7o_vRy}}RZ1i=dqnehvCC?SFV+mHZx*jZ`~S8J4~l5| zfiLmg9%oMYEo|*_)2`UFufVR25ZgC0QoS`-%43*SjH0Lq<@DxjZpR*fZ@Qr%xAHN$Dr#u@iW0kud<9@c_ zrlB#}gVk6kcr}_pb7CUz9}H#Beg0uhFr-X!_=NoBK=1?m4FXK6clr|}PA>FKRC^%g zb`(3&h!P8%3#-p1WUg?GYf;o08|Mz_b)im5ImIujNw-oQ)TR(Ttf|$SKJf}<^vSed zLQKgNR<*QJbwP;zCQ}CHVg_X*Bs&Y+onbm`^G;`QV>gVZOyY!ZJ=BQwr(Ha?V49Z_ zj=IUF6y4HMdprO|vAh$DhR)+r`$XdHL>zd9D`dD(K!W*Yn&LpvRwr5&Z$vjXSJST+ zI1j&c0l9}VYQpj)xIgG8_OAT_nli%<`(9PPWFl;-K6Rs!a4jZ}5q!dT&wD~OX&MYr zi4=b^IrMxX_tMAVnORH7F$zyWTOYWgn2tsCZ9m?{3R%VroJaC)P{$1h|7}Y}aYdaR zj7$-$z1D&|{n5KgxdY1eX5x?e zGT~VCo*$@*xFcN`rL#9I2ko>OHJrI;fiHqAp|A}vrPmK$+lW#DaPuXItQ?%d6i@Ll zzq`v-Ldc$51O$O@tk1)?Iz)f`fq>7BSH0V!@6pwso!`GB2zUJh*m?fOy$bjvnfb?k zzkA*V<$k^N3w!t6_HKAi#Q7~Hv(}=0T@cR}6dzzhgEY#09^yd6^$fVgp>d zsF^M%@LW|Zb6k(M<9trOK+cTR#gXD#I*DqeJ+v58q{5#1rgRnq{ayr~a!6OhM(oL8 z?C$Q_Olh6zZqB3hC|(LzdqBUJ^o(W~-ol+D!zN`J8& zrmX+@K-X&U>63g%7U$Crp)F&o5+BR02<3Hwx6c@mlMHxWJn6 z1rpE9oMQ6amL9z&C_g*5{$+puSf_dm8CsN?68okMcVfEo6}JIZiWG4_go;9j^r*21G}Udhc|D zgTpbboiBy)Q#O+UF@;znXB@#N#qD85KRs#&Tg9>F1r>z9l)v-VYP_OnB=+7&hfn!T zRNO03vx59r;-!6EN6+f-q|G-b5UP^2&%@?G@6P0?F*Pgd?htK;m^A6z@+TM^`)Pnw zaAh((aNry(x87=Tq7wRG-5<3I&BMG}=$v(DqAAA?hcl z^RLaQCNoJ$_nre|atYx4_5U+@d@D)mFgr8e!3O8{DwRO-Tgl!6`v6O{B4Up=m<}g# z0=LJB4YDndoS?{<)q|b>EW@HZsN!z;f`#iU;Kir_`kT0tkb&5@VxooPu*^BQZ9(ukqj2HGjp} zHJ#9XLuPTBtVB)B3UzuG^CQ%(b32zqipKd1WCt_qjo*JG=A|Y=+2k8qn?hl7q$$As zVwF%d<$A4#q`c?HS}0$rVq@=;l$*@Jx1&ZTE-fWBVoUe9sLBBcvKcCV)5@PAD$^oB zShl|3m9)GLgqPr{aI7*!(zbGkHMQO$qLxt)@}@Dc)YF%WYgMWEcTJ0Fm%yHil+@iG zKvlpIG_}<3$~A8)g36U>lJ7p`lW}2d(T#upu0z)sE(jNgDZx^iw`(T~U_spCkMRni zq7$Tdm%xg!((}{ndHS!zxKY&p^`e`$9d2!bw0`dJ56!4`l+&%|vnP7dp-QVyi89@s z?6u2hw#{{+zWQxz_Lu`6+Cd7a(o58STP+AvtHPC50&F416=>ro{C28yjd z2|7!Z0CLh9OXeSLt*+JVX$D|alCiFi)waok9h*{Z6UAFjL&XLhvsc|*@Ad{~b4x&7 z#n{Q+`X-t8QFDW=p8dk^;n1=dUHbGgCb}Pd%SOw{VmaOp(pT(t*QsCl)+Be((fI=A zsi-u{nRVS$G>O$KlV(z#+ZuiDL)0k^+%}5Wz8xNM_|qt(w@_HWOkJl~Ai2b`J}wZp z+%c+?w0z8X8B9l*YNk*J5WdOIfI^zZA?qP*T!Fe?sOJi7i7v)j@T+$e%Rr%S^~yrb ze~>&H7ZLknAuhE!C#>cTExh8-=XIDg{NLL6G?>9+*8;~^xKGVUn2ujm;Tjse6 z@|ii!Nyvpm%*?14C(2qvY};npaW{5fr$NRqL=sCt2gI;YP;qH@!>Wv>h&R}Z85tOL zQg5BIX4L5K4^CKJTaoI?y|fib#^(~D23i2VWY1B?*e3$K6pnEiM*?`zg5`pUU&{gj z!87dUGH7S*+VLIS?`xl%UymLd%$||a;3QDUK}~ieo0UKtqBNny5|U@^m=%*Gweu`& zHKOd;4~t$nO}{jbG=xEf#|;m+U4n30Joe#lUMIuT=@1|F`&a63@ihnip9Q|@b!v33 zGE6olW@fpnSKm;(O5ib7@Ns$ACarrP+|uF=_U8&6mj^T}{qIz(Q>LtO6}BWYPqGMT zWuOPG+_h|XRKlv4ZUcP0R0(lRJ7ju{?syBVW?CQwv2kaMyD1=FThs6?1(qZ1XA!Ti zd_rUB0!#a4F(^zw`el-sU^PWoEhmkz)ytx4TCNz@cn0o{tlaLbybauO07w7N<#bAfqd*!jgj3`~y5NLP1*jb2%K>rSElFSF54FN_%Uh z0ch@5XHv~>y^r}XF1(Lx^a0+ywY3^M_yC*3GhmAU2GF?mJp6KlMP7KGaQSDA7MnB? zz!6dp&;WcX{+a9GGW(C9g7Jl$&l8vbE&|NF&J+Sd3C>9%43}b4zpL$+bmQ|vhnJp9kIy;oo zJxs+TU|xbbi*z1+l6tfNN-kw7>&VR96B&XuOIR8Prcwy5e^PDl#s8Uulb$+_miV=x zPDnpPT*{EC5K({^9no}(?ctaa2&U4*bh*NcxruZcZT+$;TIy?rJ4N}OdWxn7cV>joowt!fPV*3lX%qS3sP<_WXD4mgd?*gy; zU(DU7L`C1p=28`o4W>EAba9A=aBCw&TGJMcFlPIvT8KovQ?!&k+~GK*`f|kq1d_r@ z?3)jnv)MNPgbSm|CGlv@DP~fW;q_2iE+9T&=k?KO3A7#{Z5pCCqluFU^g}zM=lj>ijXQG_5A$os_h>OOAca-H_5h?mqQ2Zf?)8FY`=UX2}94wBSwVgkXcc zhXM&~t)MV-y5?px@@qM5^H)tAS)9+TwZ!bUIparJ#W$l+yN$ZmALlQ2@qK;a*W*@7 zp;yG>4%S0~tkBTk3VjZ&{)xxgxQGqRMCBbnJG1;`wz;c-|5{P)^)c_7k{hrTZ-vDL z!31s#^e45g7-XzsBZK>Kt`Xjxc=DzlHh-m-g7fk?MvO^R+kcn%I&S@z7Au^(L z@d7WeqZAX2{cNaD%2nUSE={r+3bodpRgpQjOo}!E2g9=AG|?OX5_?FOrnXvP5RWJp z{xjH1&$duoPXVk*DxLVnr_Ar)ukPo=r^@w}QI0)-IXs^_h!`Un4frsBfE%=vRm_AG zi+nXNJ3Nyg>%8x@%?vB6#h&&Ouz#LQI1Lu+7eE@eYkn|QuFmPKRu&K^W}%PG9lxjC8#oJOm)Yo%>)CdOKzz`u?k zKR(v8f&IEXHQP;2hu{@&+$5uA&vS@LZr}Pq?VaagTm~+OYDro^tbJy;3Zw;2_QO{O zJ;Z8v;qRcqh01#*hu2VrDyM4Fqg17NVxGElGOYnkvRMzNJWY4ys{`-p?{in;6|pUX}}*7AG9*M|fl;`Hm%!|J8*id9|VAnWROI9@D2 zl8<|MK^ImRas1bh=e(=F?f#FI(rv-?;A_P6<=XxUP@OK7()J3QG-7=;|Hzk#)G7umJziSlGtIOS4T1T@{juTieeJ-$o_=lrOIxvR$+DlEAkQLLFyfL5 zrq0u7<5z6q#UQ;)7rK>6{3vb>6=fmltZA&`O@@B##Lu{+T!ZDz6yTF39UyTpc)x19 zXv<&?XwzT=akre|?f;6DIvr?LWa5N23#I!Mfg(|w9Xj(I8(*}u-aVBYy4AY^o##?X zz?)y8v55O(|Gb?wi*EagX$k+HS_8CT0l3Bj!!*Yh)=}qA{Hgt2>EG`})X2++o+zGf zQgxMb(a<8wY4(ZB{MTEpfK-Y8-U%+FXsEtBA-Zzd8ETP*x(l2RIGu_;iTduI9aeBu zy~Ae3|C#`lfr{)Nt>sgjk~J+YkRUG&P1|czKnvO z02wDTH@2Y4N(fqCZ5n;gWIcy+M3Ak+=8%kyO%eZx1}2kH8aB3`U$khd(DAH67&?l_ z?XdNspYddnzMes}PrO6^LY6~-?T=Cj=|3mPZE+-Ka!&!3rPFpLe@WTtT>^`_r<-W% zV|@@lwbf1W`}m_&u#9b66@VHl6-5J18wVe!a4deuXy2<_40Hd~_sniH?x3n>gTN=D z;ZK?>&j`lVO>^(A>kY=HPD_ir?B2VdHSoI4qeTPzyYDIjD|q_Wq^R0Q;yV^@dRui? z#_#J>Q0x~LB0pHoV!uapHSB>nUf~BRCI$jb>U!HwVvtA0Q9?6KbZC!~f-*Jf1QxOg z>B&Pp&2xJMx<~@Iy!wu$mMJU^k1x~)V~P`DsgJ1pcM;gl#~LZ^`A&hM$y#VoPZ#bC zh$hiK9(^06IzO>wGav4z(HmzX9~3@+T|(`JhX_lzWOYMWfWNQv&Ftys>a&t}cH8S_ z#wFx&YN-2O3*lv!C)u$gR4+y8Z*0F-nei8D3yeWQ28744ol6g8iqK|l%z0Y7Y3PJdJ*)EXIVq^ zItpdWpi|iETEA-4)@zteo|i&ly`QbEw>IRDX{~qUSywy9J@$gvT_`~8^M@&;l{roax6NuN9!AdpM3BrnQkGb5% zeybMUa~3-g!@EsMewm)g+~QT%jVkowtRkMfAjWI_Y?h>kMuH1%$J%rOs2zOuFi*c; z6(Vq^^BW}{p3n2m(KfAfN+|-Lt~CcWYbfv~aRK;u6g9lxa&3pyNAc0Qhg>wn{_+8h z6k)xU2RVZtA48g6P!!S>TxievWgQoJ5%~g}9kHY%^yYEZ2fB%Pjwh93YA;D=s0cI9 zlV3=D(cgDLH(}Hj%5U5+DEQguh`)aWzC7P2zuWr0H@`>vf~ml&yv{KqZ=o_9T|6N_ z7b|Qee~`!+Z6PKs^XB}pn~~Vh3zJR%F2Pkp+5GVvNM?zM7rgFeNOV|6TcFZ@X^xOI zvjNWryXS>0kf65LH!v{ArYdt~a^ia0;O06d5Sn(4<#!-SytZdTNZ_-&G6FYX;d-NbvnD4I1rqzNc^rrA>;4h~PG_&1prYS9kWs*jN%F@R#DMs|XJq3h($vc19L#c5BfZexm9@ z-ilnH=)ZrtdX@dnhbZ6_nyNADa5}EOuujY_@ttY0P4Gj3=)_gNl{O%9q-vkGho!-o?lB^<;hk^KHy=hG7s zf+*Pg_Y0r_jC{RIw<7Hd)IH-^MZcqW~gc!=GQZ_&bohIgDtiACzBszACE1h6c%o+I*bcuab^8p{?%qwW$<5N-VEh z`$#((peSqG9@9ANlEYOl5Nyd zpF^sN*r?_no|$Ex!Fsap_W<`CHo&tuY6l3jH>pEW=39&tfLy`O&SxXRdL$gn*%#W& zCoCb3>nnR?ThgF|wM9{YRNY+`!`FpE-b5NgOJvEWmd$FPdy9i}qC4v3a^F)>GREJT ze_;aswim+$i*bbNf2L)l6=FUrerU8`A*gFWb6{Wc6?b&5x2w8D1ufC9t1itW2fO6r=^hmI-|8k<R)qJQyeQd0|7kBM03l_iS%{^kimpwk6}+#66*}A8 zLuIo{l1lAFsAf_1HPO&x)7a(-66%B~fH z@^%WuZY=Z@X1SGE6<0SGRMzXHHLtYYJf)E<9g266)jk#`?rmD`D6}=`^wMj1hR`*= zY11@An7nr`+@LEK5;jE>KTf4*)#5|&dDAYhgRHIn1n-zt#&NC>Xy4VjpQlHs?2Z0) zq!?EhxZqB=uJ93xJ8RoO1vE8LGAJD3U1T|tiw+jh!ilZc84U#aLHPO+nprGqUkred z{HXGjE-drI=n7=;lZciE>hWU6P;@q?%I~c_%i6ZyI1rpg^bIBPC34f54qe>2OxRdi z!IYt>A=c2Q8&O_m7BfUx({+5=9s6qJ%M0A?GS6PKJ*>%T5OYiQ| z%4>%Z?&94+yyLQ30fHr(izhXvE!5A4ovld*rqJ6bXIwf7xHPc}!<$W_*IF;89)pOn zrLtCg1hMwIf|CX{;8}$oEwqr*cBb;?H%*u4Q8%V=`%iCMgu^gLjyIVWqkZac5DPu( z76MV@7#^hiAW>m{U^)jS^gv)zF+{)%bn8xDL{Pr$qoOV0yhJs_E8{$p#ha{=!Qy$6 zHLkiM0N@C~x-!Z%&@vcL3C;c}HwT6Cg*ggP%GNOYS(J#9f(YfN^%Z$*6R%5jJu>~{ zv3UBeGY>#ZF)vVnM`H-BR3Tla{@U)3t*5z~8EY(spBjIUH4_&*`9MvlGF1H}H*|e7GbpDL)ZN`+| zH+W6TAd23%K>w}}f zo`v%3f`lEma^z(_2vhG(Q%|0OIOWYHW__nq|AYO6#btgvc+7xC1Jd^^bc$Qx0|cIK zLG=_Nu0iz-NH0+UhzBq%v9YKrWq^gptS&FKft){2QZr_pMx0b5l!g@ z@ZIMBW1b)afIa{^=xsv_wGK9PAF%9Vl~88s57WXJPRevPOo-Nn=T(#JzBP*38w+wc zKJ;ialG+&w%q&CUTs&tjxPq?9uPcyi4c6f&S#w;6rwxjLN$}apts8E;SJu3U$lh8W zdeMBs+~609Uo7P$(q882$VmbguJ*5;49TI)XG$2^C;`~voMFGv7_;xQ%@Wb^Nw%-u(}C(8PD(5uvs@;FR^Nk6z#U~x z(=})}JPR_XBho6}nwq$0O8is6Kk^oEC>xLHQVFjTQ6Zb7i9_a&#!x!QcP)$y*aoVM zl%s|C^|9`aWoX4y{g(Q9(XoCG!2j%h*)d4rLFWwwpthYD7J@ksD!{XKK=qoDOm_-^ z9%ss%x=>VielJYYgC%W3k+fk7eZiqe=Q;1Si{EO?03R|U)7c+aWkW>u=?1pv&e|<* zc~%PVV{{+cd5`Hl#s{<#IX(j2A%F$oqGrO{b4|-@N?@kbC6a7w#?zyZz@17S*3X2f z&6Z~IVwQ8(CWV3(#h zcSH(~;gV2>Lp%3}8I01yDI=}h)qcB;igB{}?k;x=%06;=Mg6A#n@56k!EE|j-vD&{hG3HYk6c~oS@ zDz1c1;y=oFX|w8;E_s3lyX#*}7h^mC_*GA<)jpUx|E zTwkl*CCYTLqOJmERtiDlrY`|XrnM2RlocOk-nf%H1Dbj z-Bi0gjA$|AT4DNm4XQc`e^Q^yQvd3-1o^5_be>U(E%0mu73JgsP#=W8zq=o-+9CyZ@hrSw`#LUVD z;>H79294^Pv(66Lys)edk_cT?sv0X>VPx}+_qp>iKH1_@FE2&SW<#~3qZ0r1wL0wy zHk(N|joI*JZ_bxFJV7^`Nztgw5@XOgx>BcT0aDw=;YL_@@88i{T{YHQe$5uxsxSk1 zYh0e{7CCQxqczXcgTKp6Lr)ei2DIRiu0q5K{{qW9cPS3QgR?x1IUr<&%75e$GIm_= z7!}Zh=d%^=7^Og&UyX~#q42GG=A zPXpObUrht4^3OxmIooztHqI-+$<@&+hjCaC+bAixMwt?eg<#CZ68@A^PHVJ;Z6(KUw^oahFxsy zR?a(HCu!xzX@QHbhN{p6#gP(ujNl5vx?A9K#&E2zJoct)vwRBA@D3BvY^#pBO34FG z=B-K+r|L1F;5dq0y6$eYVogF?QM!xj)33aw%lk2k0bGr7Ie@D%v-xja4VVo#BWO4% z30k`c{_$>J?EBG$Cs<7k!_PViH+Y}h3(w{@HD#n*bI^zjh>#^b3hY($ghgY=X$;(n zC61$aqH0H4!cfy0tNXKgv?A(Tlt6&wEwm6I2>_PQ0BrfZfjwlJ4)oM5(xwM8cUV^= zY1;*s|E!S?v_uZXe{}(H54gr#DV729URovx0;B*y=I{Yl*U|qP9o8}cmjlcz?NSTm zKpj9 zyyJCshl)nv-!-mZem{PthsU#WkXd9%lY+Z;LVkw9+Puj%Qx8;fcNY>j8(*IXf&XqM zrns6(|6jS8buoBkbh~_N91=jnTaay3pc=xd2&@llMN>y@a&RR_n(1!|W=m`hT(we42;xxtL1xoE47~V}hpoQpdb0&*K&O_pt^f(3Qxwn%RIU!Vwc)kq#Zw^10&U9n+j(A?dcv{h6k zZ;!#*#j_H!3~VF3+Nh}xW_g)XC;j!L`~5edJfgBwo|*AzTa0u38pBVhfkzS(Zd8bw z$9|h&LwJO$Ofpr?+}v%7*b=}Un#;GR<%amvF&>;QiW?W{YOdR>AH#zO{QQnSW3-8= z1C}f?1deX7RO zvnXUSkAnyf=Q+Fe4q)$7seA6(wUpe8P;(;04dmaF9LGce!vBa!8)mEdab)FMZy@=KZ%Q)aFfG1k8E=9&MU4HRrtgU!fy)DF2w|8_XEX*#wIH)=f616A# z%-qDMC9?PCE2z2q!>_-ha6py$(t27~=GN%&c6}hZE|$<#S|PBieO-=PB0WPfTb*cTzPfw= zth`Pw@!bw^H3VK|yU6^HmIH;qQWeU0=eMbJoEXNm6DO}0l>O1tknLy^^f2S| z+@29ecRRuohpb8kA7eV5L~M%-iwj|(=~v5QmR$6M3=HtWULcutkqLe?ivxM4ljBo{oZgqY^F0?_UN>MJtSXu)o3A-q1{A~FM9lIf~XU$ zfg{1$*5eYnZ^VYu^Ls&LtwfX)JLL}VN>a?e!NbBGYA9yf1HeTxl$Q~+MZ#!sn zrb30qrVgqM;VGyG?+p#kCnrZL~2RZYnohnEx!p*B%u$vT@2?8&rYZ@A(7043Sd0XdcpwN0{W* zJ%Kz#t7n*qKBIpTk+9RY0Ao;L(2>5q^jNml64s z{-2CUjOzbnMB+aFWkk|w41wYcJqnNe?RpT=9kz--8V3AQ%+j)!SnB-nA$_ZEWC{i- zvt>0wU}X4EO&uWBLg4a3EQ@bqJCn-Y-m!*M^D?A?9Y}QBe?c36=B?csM+HCw?uE9&3KZp zWXT)*1woqQu`s{8dq&~kltyBqNnlTm_fjkwO0h|ou(GDYU*rZ*yFeJb`YemErW4Pi zk5dj6Ln)-KAR!PST$_Ks8ATv(sW>gM(VuBMw%)j4+1$olh<* zY3!tuoDx>Z<)>HuMEqK4$eQ!06Ef+00ji0gE&iKQV8Bn9uMhq@62ZAHn+P?zHWTe(T#p`_!bM_Qi zPsm!Dv{FZRBM3xYo{xk}iL>O#UxuTYQPj|`#jv`%O{{G1p_76+sMEW`WHHGh%t(y- zvGDLZLCJAzrBmsmtGY`cbYg~~SkB)mIhXjnQk`kcVOror0;SR}xl!Wi_;TkZY{Qh# zwNH;$h6)(0jS3=D9F!mNyuL;9Fli>Nj{H@<6ePjxG$&P+BZZ|_9^PT8IZuh>fOFVB zh75$#7)A&LHf!0jVb*T1^-TuAA?_=22O~^|*gG1#PdR5ky0Quca8}I zgU`cT)9(6cG+QZB_~`*jb6HvX8bzulR02@&d}0{+&tuNj!%lOLjK)7kVts%DKBCTe z53rLY;DX;LE%lq5=GSPUuWG&jFd|_c07j%kjz4=%J15c7vD^YsjhRgH(ZoC}WwRqBchm+|t{`6gvu6VzBm1Yvpnz#ya|^Q{YL$W0_-4{ZaStWa&qgr9g1R%?AUG%C)y#n zxn@kA<&jK+piWe*nqsXdkLBV99F~#k7}o5t4(eWZBTB z2LywL+2PZUKDtK+M&L@5bDpNG$o~coLO0GlW4E@`tLmW}tq<6H8EuZXQ$lgb9{p0K z7tI~s=mR5&@!+SNf!Tdk#kR@tmH7nas?^!yfvwTX^&BA4>BmkG4bF%6q1Y-i{a*f3 z@1RYR8V{DoW&hMFTzaT?>VUQp6t6CJ^w5$&s5%#_A%i>d0pXnlxs>0Y122$HBXPT# z+xzL?^>pAut-0*kveSqKqNtmYJ_xAl785zxYV3yWK_G_rP*&q##Tc`@@Y?Ivn^}3m z|H|I7ufQ3Ni}9?%2NW}klcqEswbSX23LmvpJr=<*nwRLn4FNuCmun-tJ2+r0a($f$ z+U-Kf`skyxtsQS!)+Qbzk|QHd1OqeERVWVg6*GO7BPATnZ>rn}HgU6&@ zQfk(0+wc$FipYO)k<=OhE}|S%2!rZdIP=_#kXg-aI{2sh9}w?w?w3qr2o+drNVZ4z z4lS+wPZs%S6Sdk@*B_iPK>VRqX_8HQDGiJw3dieX;&Xi4?Qeo$D_~7vK`d`jow$@_ zG?+)gqfxs0f~G0Uq3m|L{;Wh;);Pdr9v?4*q8@@!R-QPsAq+I;!$4J!=LtFWf#yE`D-&#=tw(cC*gX$3gm7# zJt%K?bG(r-s?wSisF1H9Brx+6$Z8!mq&^x>&li5P%)yC|hJNSg;0lzKt}LX*q8F_; zvYG0e2Uk>1lV!68321%<`q}ALC01#N$!c&-`$yx)P{=2HP)ppIESYqyL^a&76=*RR zf!Z$aOIHEcSSU+{8L`V!xNA2C8lAmBh;H2x@~Khua-P&^;-~ zp`|H+tU1o9;I`n5kblJ0%w!UZJ`*Ln+RvNg>Gbw^K;ZGf>(bNeW^{nW_eB}Xlu`-j9>DlCTN@QiQ31LjjoC11-$7>e?9fs=Xl@nl3 zaOg8NtKi;b$#UHw=VH}|epgeDrpb2b!nWu`wsjkc73m7;Xp%>WAZd&>vt53w#sb*N~ASm1g_=3&$;WO8lpYiaIE?1_I ziQPUlB3D#LI+_ENGC4pR3ovvBtJ5@F#Cr}M1L>$gvLjwOVJ0~f16Ad+d2e2RE;SE@ z+_XEYCHl9Ac0rm=Y*daqC>)6OafOVR{XiI3kQ=0%bi$aNtJ;%W_84Y~v%QfEzT>F- zR({+kM?}~oZ>^J9 zGPqa&gVI2s{tuKU+TB_cu7x4}zo9hCFaMx4Oti{(o^JzBHiVaFDHGii;L!db=4TRu<*#>rmLNrSj zv9d_Q+`>8edOki|96J$5cIKph!5Eo+d1w%eMJOju;c!5{jRI=2_<0O6{d06K0SH@?2L*s7=TU2QkvH);fo zMLx+|GJ{F4LBU!-F44&fKmusN6hKQ(D+`=zb4Ufif^Nrox{CWC4WnJEZVx#_i8DbM86&t)xlf!KEPTfh$e0oHX1&;aVS zf-8$F`k`ZQHhO+qN^w#AnaQMKoCxIbj|&6((j|^k@R2_*R!IlAaiXdr5Hptoj}!MRrh= zPr;SQO_ys!DfP6#w>L~uRkw*YIZpQR=byMI-JxW-SMN$jG%0$5;L>-~wW{ks$jk>( zTR%6G!9Qk(9z8yH)U98p86;Z7IGnAlveRxX56WapkSt`^Dy@&Y#JYo`xOp zD+nFjD%MgX{H@5MvoMW^sryIkcQuN|S%+Hb$G54D=7gkkXEn8g8-VwQewP3`9u^FqS)_cpAdB$RrMzeIKV1)a z0oC{M*^s#?<(9vebYb78s&u&IElC`i9dmT#x|}GCU`j?#*r-$qLmg*+@_aV+bHrXZ zk>g89H@AwRV~W#O@+_b-ScAR`XhI0YnucdifD6jka4lD`Nkdv7atJJQX>4T+-oU>) z_Tn8FmBL~u-UgYz_s|X@y@cg}^HSucBNCRHb%k{ zPN69tT=+1zGz^Jnpi^ps#MbnW)Tv{Tx)H-en+6@Rxr!!hWEC#SiMe(X6F+7@HwCG| zqV5rBS9sj8J_*G{P?0cPCHMkg2cP#XL`Vd+C;^#xSp>jf=mJyWE_;!sl)y~0L3UNZ zPkx#~G*qb{j&UAApdr62!nhGx%8x^nY>Zs1crAJChou#@#}C^@F3y55BFIjoI=J!o zPM0HieO*<^SV7K9sMnqYA67Ibp3^bNIbnub+q(0pA-}NMaN*F6f(3`P8#se745`?5 ze&e`vwNrI2VBi{2f>9)z?Up!u*M4(NMDMf5@|@;Do6+L|?2CA}69BH*MEI|3OC5=2 zmo$%E^Q(jJT+|jZB6NPVDeNHD|A_4CyAB)tz4l9!+{?_IjBXD_@&D%6388+e{&|rX z4XA&OKJb2XrVU;DQ=tvFa)bN%KVltk#s3lO7%t)JK!26x!;(NMX=@rN z!zGO42o76~2R{}1pyFeT!{lCpP6hM#$ zXv|5j&T5JWf-}QFi%!SaKc@~*xU&jgLyzL}{BN{O@jniS(e>l5Gw>7GP4nM;&Mv@A zrjs5e1bAD8o-eJ!$$&TsK%RYO21oETfP(%98AVejz1LFhdCRx|hgyg2GF)x65iazX zJql|D$4t+Lrm054?nnx(*QI!3X|TQ+(5U0$AMKJEM8^q(6A6d;)ss$DY{tCdRnMIY+P8Gxz>RQPSbh&&gA*D^UNIjGAhkqesx!y$*uH@hN&2E ze^j2^y{d0+H}g%^4#b@7+FoijBoc9R7kK>vutS%o%`^#5eHW|mmN>M5;yPdWR_B-hdpAAFB86!omgydVPquO@B3}!!b zd#iCc`q{99RoQn7PW~UE&iIuc`6IvE<1dzN1_r+q9NAgI4`$dW^I*3S@*GePRHm?F z-a)$y9}Y4KBDLOLtVaHx8n?x6ZuLs;3yt&p>i>EL*T;j+8DUH#YxrBo`8in z;_UNKPyOQqbr`WEDk8B}y7i>RJn9>V^AZE072lCuH#fdBkmPn4w}Ne|4~)1*6DfC! zxF)Hl+M0T(m%2mFlqjM z{axN+s$mk5g*0VSe;3TejC=^4TrAj@VGQ-nDxQgY9Bjvo>`o|oyvG)+pFj`{KKoAr zxyv)X)lvSODyZ8@r0w$olef1MUK1mAV&((75oNa3EIrN!dhi2nAr=gKQEq9gQV<~I zfUsGhaJF^S@^5`<(ZdW^vvxG47*Y@^YHWGYF4V@~8A)M9cM^|jvmv%T<4J_5+G5#W zi+%_^GnXmQ2bvdxtCBN;E~dsUH!~%ZUX&U(PJ}eU9W( z4>S52C6KEh{!wkc5_AL|7ktSO@ALetq| zQxR{tx=YA(m|Q#1+RwwSGcu4T_~0Z-kUhwr0dXVK9J`P3&_i+BTd|)?+6lh_0|Mt< zO;ciH&$6g;1D)y`v&dlQ&DJ`XKD zycJMxpzH9hDW#zJe=7Vcq7lEfrJCM-u1kZkHZfOCY;68OMW4por`00rkZ-f} zLbQ3AB`LOJ$!devK%xrkmZVjaOnTNX=B6o@8)wF%fpW}-`O_UiA^S-*{1VQma=}sW zUx3x*IHGkqc(DXE=u$As3AQcB@@kg#CasU2*36r#@tiMAY7$x4XT=kt?U97?MBIMxnAc&*yA7a8dGlyDlid zK8+VC)J8~7ZJgb`*vdC(J=I1Ex~k#>+=7v{7jY029R)S49sWPypD&>_U3?du?KZw6 zniR+6{N(E#ePTM&mRG}nmYfoxMF(0>2VXq}y4m9j!LAu7+0p&>4)oyMtt^8-1GPA> z75@?6;JW~QI59mgNDhlf{=j7LeXnW_a8xv~x8vY{{@MSRJBJtGMtgSOy9t!_@WP`I z0)OH={g-Zs{GXcV-xsNW6F!Y7I02`pN3Q(O4A)sAz&2nkc!>3j0cY$tMdUE$5d6wt z^$r6G&;!xga;P*4o@6`6Z{O%;J7K4C_`zXah+l?%cVCX<68&uj{0=uJZRb{hh4B}X z{ru~b`3>78AkIJVj)wz=F5))}FKJ31D6$>HM+B+J8U@!xktS;`xu!fPpkf(yjNVRK z4hrxKU8GsSq+F5=QTZUdtuXhRHt%O^S(?Ggyml*M4_oDjPN{>X@pOU=7W@6`aq7KM z8fK9JTXWQ)kWBQ&Y7ihAO0F>VwoGXVxvt=i&})Y9JpU6SZ&yO}<=Fm>yYU=rH~?V_ zP8$+yOQ6aJgx>xq`G4U-g*8q50%zI{xj;6E_xpd?Ajq}!r!fQI=GU`Prk* z^x{v7@(z9@j$xa^NW{@y+RRV>?G4rXjABxs0>wh?!vr)DsiRDW81`o~Q(+nOmj>%} z#7Q$;-7j6+WRfnSIL<+B=MydG;|2Y4bQ-iw)f$_s$I3i9q1mP5Bu4- zYas0t*~jnm_PV=5^gfc~x$}qb)z@q50+=!Rdw2D$R(T5^mIv2N%52VHR!z3xfpE+9M^_?*FzN3Bwm2@1WxobX z#3+`T`3}RrpQu46BEt^k2$?7TD-eCUxT-7?@MPMp zmW5pUaS(>Mh~`)Hnr2})DdEn;J~g{4 z9sEa4!Pfw(smogGvLsy?UXYmWL}Dr(cv=;FbAX`pzsQuOSd-~EoNcMtk{wsG zS#RUsRiTCFAC>6HtP4pF9SP2Iz~uU1MgInf*XnNKqd0)_}vSk0@&bH#ddYi9;+ zx>6f&S3~c>Mk@-s-F);v#1l(@NmHnB;uU%+mVKkHpohY`M6i&)D;4HrhdakvE)g4v z^5vy%vvb%G<5c0_sV3*+ZTezG6-tXApaXcMN_)$Ug=R;v$o!WMGemzEVn%6u8piaG z!Z1*=X{pX;nOAm3yEqM*!$k)Mq+_Q?a4oIS4)Jp?(L(t34L{ek*FQYf>B^5WHbCSL zF0*c*mve9iB~#w~YHp34??ejj(p&@O=do_X_k(th@ArhR{b5(SpKeI86v6+8q)5`f zut%+aQ$4JTGGiwv3xtLm@0TW4lb>M-P$n1%#HRc^f!NfZ{n-CvQzt1^@@D(%IV1Cv zs#aQ_xvDuAVqs@+D}zJh2T+DR!G^F>Y2wXYmm5$&29~)}`tc`K*=IBaQhDNswIBoD zre6zoxr2=~EbVw@dc6jdfd1=-NyY8-LV4u0A5LbsnX=e<`@uxRbdHs6e%Q$3g7 z8qn?oa93Ub=zmF5&VWoJ@hy?zIPZu0QS}{@DH0`|q7jADC#2;hG;s2(he!y@csi3%M`-S zXnoujpXkM`S>5FMd)^iv9|tGVvAHtVBKYf@`I5Hdwr82?Fhc`Fxy#3-)i z1OliPg{}qUfZ>fpSlQJ`iN@_MVu&o`2R2hbxuBw9zLTkwP zq?J?VCH_E1<*Ru75@Ci{pQ$H&C|K-|e3Ez`Q2GvSy=ZLyzsS^7@&6)IQCRw&o2z}JUey(=`@@;E`ybi$IO^vkZho$ixsE?-xSz|c~4!T^+SoExT{RdBhy zg+l69PC>ekaqYDGKj?%PxRn!#__IowF5E?YFi24n5!knRF-sLZF}HVpdaT^W?ip_& ztq|X~{n;+nZXR~(m)eJ-xtgsH-+vEyMDf>Yt=lmz)#V8{R36jlnVtQ`K%#?Fqx)TS z4*h>drg&9GB|kp@8=2}@#DVHTOFz~I+$PlB0V{eAcD8^q|6?U@AP3t!-7VHS3T6&B zX>1_WjNC~I+u+CM6xEEIY~Y-}syT5TP{ne&bYzjt^f5-p?ds{-R{5^Uo$t`*Ow6@K zV$j}Yxy-L@6rV`+nFz`mu^rwA-tvcUvD6{vdW9)Ft%w|QwmKTTTP2}IJ^@pqN!1|S zEa37g*V5u(Z$$K!+@7na$u>MkGoTob3hKi58gVQQ#_zNHWBspa%r0{>_Anxp!Drpq z7_~;Z6)=x_N1d?Ruz6kwirj#+7=gt=li`+mMVB*i@y@yrQ^>^45rEhNG8H8_>cwWm z7Jo*~zJ_6jNQO$RBQ91gIa5pPF`u6yBRK>fjR7Sf;(i}1C7%czwYgZoX zX9)ui7hDWW&fBhss8Dr#+&1`b?llsJB_drE}Lkkf_(zcmk?% z3jVot&VhCy;Ii4{Xv-eh2c)~ywhw^q{}enLEht1fH9uYd33fyZAb^4$&spGTpgjku zqy)A_Di!}{(zgT3_W#IsfH&1#B)Q(u0@XS|C*>mf5l+&ompB_C^g(p|&B&2)Ke`>g z+XGx>Iki_S?zl-Ya&WNr@C05=hm|j7a9L`ICTBoqw`W)JeLXcQP2U zK@22l)r~|F!-ARvI80pF!|;EGf-}lolrX4DtAgjkQmUYwgp0I!*z}%Z{{)Z4&iWJ! zSTbnXIX#z{x3&s6f}pG6;8DlCOs&-%2`j?|19BRi5cEMIGB$X7QB99%x{OwQ?_>yI zhgzfo+$IHx;E1jo#wdW&?@-M`ssIJ71NI9`lrei*!Iw6LO)RKLII>GZ;`YqF?w+7e zuM6lh{AOvL!muG+7G~8<;5jVdy=a6f-j(LR=HPRinp}%++beTcV&5;xO_ixoe_%ug z#D~=r6*BQny zw(`N6vyB})Yv=RO>kRsvZoMq42BD`JIeCb9{t9rBOR7~K&7Yh-d*?npNlvdaovY%- z+$DDwro|)|E3Eb{a?+rfj>Dw3@+W%yKM_+$$^VEcid6YP0a+c&l@iX%H+^aGXKA-4 zCa{j9q$(1ahMN&Q?3kK^si&#P9-m096mgMmZ%}iatr1~_&MCNvOw%+roP8M!*e)A1 zpF3XjblVo_3Pm-w9(@AD0uA;vqi#h;z>^^3CC@dugb`0J$drld@nz7IJ1{>3C~#Ne zKlq_IFq|e`EphJi`<8B^s5!@G=lJg%U^P4QyT~ZM;$yKW0%t<(?bqWQ`vIm#ciSBb z4tPNNTWQ5w%VJi;kJ+3Muuq%^8P$6-;c=Z+tWuEGD{xwOz(=rzk-kzC{eJfMltgrC zi|Q)vPgo@MC-y3BzYQWVe4A3a%}k|e<8vqo)v%_tmC`2Xs%8=Yc5BraC6Oual15+H zO4J(_U>_8UbZQ^ zUi6R^=`brBwt-?f@b?aX?}r@Yly8Hj;?IAIQyawb{E}vUue^+47pujC=BG- zpmWfkV|ajsL^dH8cp!E;SN-^bTBhNTpcBncvg3dN7rnuaoYEZ{JQTFPK?3b|!dJ}w z+}z%TJ11)!2_-_CQhyE)$JhG5lvD)AOzAk`v0Jz!#lbuZ`LVV{9E$;r{_Y5Nq}GwI z^X;CmuAlJi7x&< zoT>*!JiiEe8mf<7O}zxDR#e8d6(TmqXn&rSgo=9qTQ0qMJCR_&2<8oEA=tF~k(Gn8 z0nbeoES8vi2xn#^i?p`mPkHafbmYczBZ^$anKe>$9u#mpB;u)$hw&8ATi;nJk^lwE zk#n?i=d^>kULds1t0p5)M*OtqCBN2 zGB*hd+;(&Yv6~4tx%_iKSAjh&Zc|`A#9W&$g68cEW~VlbeJNcIlC`w4fVUpwXqa6I z=c=CL*pdxhRW{PU!6RzY{XQ)2kffwDF-~XV(c86c|86c$%}AWyyDzF?U65V}XGU14y$CO+pXrn*ui%2O+7&A8g=%^gl?g|J&ZZChQ7+iATHJ`rpXSjw8WLixX*1Pp!7)0_*tzS1=H zI-z;%fM1LUvZH>?@Sc8lq;K;*T6b%#5Aa#JHxbzJ(4HMw5?f97b2z(N?-s6aay6yZ z^%R0^Dj+Ok=%Xnq($J_-4+Yh6E5VA3?R+OlV`bFZ-RsWe;exB5CE9g>t9mYQm`Okd ziRyi=lg>hS!LM0f+KYumThirn(tPN>&eJqOQicd|e=Q*{FeO&uyFK5_EhddM@>@2y z)g$W0sIZP;HHu?J&Y@PMl@*c9QztF~V~*LX>0Exwv80g*r);>q;|xMf0^u^V96lxC zxIrx(ic(;EUaWTTS91T5sN z3_^!unLpp>XYX(~@0NM`ePx^bp=uD{fnpb`w#BgLwQ%!I6~M+J0e1S`HiA=mqC{_x z&>|VS3TL=%B9GllWOcIs3zC}4Ea5f7t*V^&kk9{BXDtvYAf~I7>sK7!>eA_ioH2I7 zQjk!JI1@6o|M=VVG5R$m13a|UdcJ?Ix83^7M6!BmuVfRUrUw!-I~*GWc3n1frE)ej zT~0yo>#b!c;Qm^|!Oh+SxL0&aEs+yXU?~0nsZb&&>om}rSlmV#buXtm$wh^3#8ulP zGpp&$a5gWm+uQD|k$t)Yd@spZ8fT?-f(DVLsJ>vwyAnc=ab>V9Jr{J>aYXg=*5TM} z;o%s*b7-USKSK=;3=`Vi_vVBgQk1vSHZ6$c`c$J6CZGs?Cxvw*)Apl4KxH2A`b{x= zOnG~8@Q2tRr$f>4i~1<9dX3v6v6;BR53lk@p-?|4o^(4Ugk`rIq=wbL? zVK}(?{&m>}aNS^U=J@pCgG@fIH0dCrlHh4cFq@uqI4C?QY5O~9(J|d~<_JYj1Z#w< z4?7!u{OI-k)xX`Uh&e%Qy;io08AXK(!R{C3{b3!hmj~Hq`~c86x%P6=LvYM%1cnJ+ zzzfpy=rv^MQyX$y@Uvsa>KL#|_DKg|z7UNe67%Cl43U|h*~;k%ryVZpzxBS;+m-aN zFifnQa`j!8GKopG=iCHq0Z;6PFJ9y@rS1r;I6KcJOkQm!Qlbt*XK5&!6|-8cog7Fo zazNl9B;OX-jTJHbaiG~$(Q<$W?KPivjY~YBE$=^@2!U zgQB`J*8Ra}G3Ed)B{ljyaviekuOA(Q{7n>#vS<9#9>U-!WPvUMTXDyL6g&PsOEV6C zdl4W>p<-w}f@GptS{!7Xc%nRTeU(((p&4tT>dyQBW$o?HfL3wT~tZ!&1}L z)L|zrj&-}cl=1@JLxDN5Q4iPKFBYB2KKUfRLbE2TViqCTcW)&AsIfBkcGAV4Ep~|7 z2QOPNpN<0&Tzm4aBX834rX-JlSxAPvVU&I=f*7?0{;Vzkh?p+eMw$=gx;?uJbaNTx zZwdRn=x1+0??pE)|Wj1VoBBc6&1o5YGd(*M_h@@ z?X7QO@_4X)nv^|l|GfKCa4BN6X)6#N>DmgY9w zCwRWwYAU(_3`45Rx(@dV`vxw&$l8fjl1g}One1h+wfQxcj6_6PAZQwMpCyWXr@u)n zBcXsyL2{Wv2YtHH)vaYRP{&a43!IY+F8b=1!8;zevW@6dH~KzLTGWI7P8fGNcsp^sWMDZ+w%PF+rp* zFfc&If}NmnN8@{0xF!Ze-QzbOhC@Q(a%S_WQk(+nTr141@M(YZT#EMVU5U4WoG(U) zZimzHE?PReP1b_kbhT)SdU0l{B#wj(u`MR{NO4eZ%!7d~Qy;jQR=% z)kWNOdWN-ob_9zkajS5!@b)}8|Bm!Cb*mi|&ULEnR>_67jV+57kQybHoH#Tv#}Ajn zb#c|JTD@bFnkP;c7RiCJs*DUvR*e2Jn*kUUAIM+vin_qMjlqW~L~F5gwcvt6e=%(w7YM>ay^2BeUV?B?@gh=S-Ks%8 z!90{*U-UXc>W4g(2htlp5|qgT<%szVPHOHtxF#$h3&R2ri#f_0ykgZ;XC?12ayrsi z_xvC;AGDT={Fp~q`Oiaj$LD? zrgWtT{b%bPS6H2ogJPa#@%V`C*NV5J$G!>X!(xRaLewW}j-s@jyDU8;=x}CGXn>B9 zWLJ}f+t*=|6x+!1`-pXd*dYx-jBy-UltbGt!{P1v!yo>w1Al3yV9wn5;84&5pPco! z#DmI_FRg{rAldydM5Q?OakHz@y=uTs);VVcaUvwUFoY}JFElB(@5`G9#0s`GeXj6; zb1*;~>0vh@^QNBC=cMVL?eq`=lPVXbG8M}eX-&nbOAYI)mrn)tg596&P5L;}Ae+)k z1%sT1yknT!154vFOn|=BV&3(nwn;X+w+&2Yz@4HHxJ)P{tW2+qUuqQRH=di$#&OZg zP>IM(B&&+u0DG8pM=Y)T32TqviFQ-8Xxj>wH4l}P3#nf#J!jVwym(hl^Rm4k7G6=k z16{+0JIGkI@qPAdI)EsU_!5bZV4=D4=ckbMyC;3L?Ny#KMd<+^#m6^$26bjzvde_% z$YZBEa$j{~!h`gtUwt*yLok-c9OICFEA&qETz=J)$u9odvDARZUoHK=Pf3-5&*@p3 z)c11}C6R6(ULv_MB4aMkLY|un+md}*ZL~6pF)#pJc<1QTSgo2X!Uh0stG_vFvLftg);JfV_kpC0PUCk%OlC$hDM zKhKd~@~?N@`ViGYrb|xjzXPZpH0ZsiCdnyn=d7+%w#qA%!US4CKxgkf{ZKp+o58Bz zB?S&ZNmm$3tFpe4^Sr$QG_1~6?YVf)6}XI0=Z7|qTAQb z6Una>ge~ouEje8Y_DdI}Z}E}PHVEU`3AR*?tpbNaq?H(jcIH{cYGKf0OxP`yp}5kO}Jei?lg9UKZ$h9hfFdEu`mt+}tu?NR+XaGkFdU zdofLWQ*3?8i$b3W#xD)UVa(Bnc4J81d2bTljS#Qxq0Ti+?wc-Y{H^dBas_)p#F&T_ zGeQ65YU6(J0DQx$q~^>RM+wFE=HYcgWFyRvZ`^fr3JUf2U{M8@Oi4VTkPmqth%+mv z9oARBMzJ9KqPn20AS!mkUL5j1Wj(@@W*gUN%rB1Ati)F_(AYDqk>pYfVj?Te9iF)a zUer9yN{(Ds2ee3#U8~L(B}PPPbEA2#H;LhoV58TAQ8_%Tufm;u=dID%LOO@Vy{7TfW*Q^atZY(AX6k?d}w)?7k-9jnj z$(4wieX0w544^;lmI9Y7y+Bo^*|hv2K>!OXFsB<5;*3u)ys8M&hOaz8P}>L0w)YFz z002&4N5tLl{+hK1MMIo7Yjd@1JCKa!VkY>R$r0V9*v8>=`<;dw$gd+Dw$m{SqI>fF zixt>`J0zPC02ii&`*>XXzU>IZGW48s3K#bn>7E!EOnK5Vg7WUx#5eI{Pf$k_@VYCe z&ef@wtouytc4u8c!xm2sK@MW za7s@bJ>IaT=!1NLY2RbTnXEF}LE+aR`Wh}$Ucv9qr+$IXnZKby7R3P0^u==_|x{($M6>8e)u9%6CHG^ziVC8Q&liVe0z%P+V7tHB4U$IzIIj#EVX z^iks1FSsi|t=uKy4&y{qr;ggCX!P@RiYCr|6AHn|fMt@}Snwtdu=L_{W1I0g<<$DxG??q=q*sOD$BaUgn)a&`ZZ|YIl*Gy z+~oRT1aDw-_kk_Lq3EKGCZ9i?+n|c#Gfs+~!uRXx$oJ#`|bW< z-K(XB!&S6Y1yzOi{<04K&R~dU<6rXs3L}7CbJ4;!szp4Olvm>X79kLV%Jp=On28@t8iPY| zpWbFwPxROR+uh2Yo;Ejs>{X4Cb5hio;oN1Ix48x3y-;}thS?n+1PqA%v#tYPfl&9( zBCC;E9pujJ#nTeWJ`#< z5LR1J5v9H7$rtvNDY)RhiV~Yr9>1l|Bu|lvfW3F8H7coua%APrSN5P)k@Iq4E`N-> zzGnhf#40Ld=ZvlB9s$X%%v7ZbL%X3{koIGhzEM5;HLtMK3d*2&w({_!y9*RDz2!rO zYdznt5!O}uWl>yZGvqlb=_ZLi^|*R=crhu`mDEm%S@1-0Yn|MDaeP(PdWpQBeebA6 zyPZkwa%rIC_d!d0NQ|j1_eT`TY_EKIkaz9ITNTy56F+rX=ZD4j_&$g(rzyc~c&!=M zwB3KbOIdA^pTGUNK&ns4X`Mg+YK^}+5}|CCV?w8E#8(k!^zAph&e*j9$twGtRv<2S z6_`pzN{sT42dm#R+HK9`THMRJB#) zXQdStGrXteH0Q)vKP}D0eSXuihSF?$>IY}iA3DMOyK`0wgK!(N>Aj@;XP3yUa&%P> zCTcXX%n7#w+~T6i!@)c98Or^3DDI)GbU2NMcS`P`@6)&mHN48Bu@DJNq}}Vb>()jk zG*;)~m1`IkV+Qo!xDF!BUd^RH`AMh(5ZA&JTrysacjR^UsJMbw3>EH2xsFDkP$7Ne$$@&pV3GptaR^u-0vQ_y!r}`Ru);Sv?k-Dy{mqdS2TLr&tsHPVFalaUm9aR)mC5Z&en~zRgZkAo-w$xNtpEe7 zs}HfTYq&%AmoagC%ML&M&iU7;D$zOKV=z3eh1kWkrqbQL>1Ho_Z5<(`yLIkclp2@T zKjV8X5lh1w!ON6|A(XuZpg6NLmwiacf%3LRTm|T9p|@StMWrpeId0C8fI)z9$l{r| zSY;K?S^L`Ux?&!ZT752kpk)*djY1W81AK&YiS4s|Shmxm{Xi0m(6-P<(I8m<`c|Ft z2``55y{hFykN5{~oOD;xkL#Z>iu))Yq=gkPVPxnCJB{?3j{95zbPBYxka0}~sVnq} z*2FF}>V!2(MccSOjD${WtlDG$AUQ)zhkW-@A+ z0WZo053O6Qc!0o9_MdYZ{7@-Yd|33fU43O(5F>W=tM5bz0&?Jgt?gXvB})dHWA3A` zkgVIP`wzW4AX$kuM!CHb8czzztb-rz-l-6x8G4oZ8$4P%4XY#g&r@&(_N_+|=$I>K zBO2hMQrDcgMTw3~l-g841))HiNSHM=tH67ZLohlHMVI43Csr7cv@n?z%;u3iJHS~s zXDWJekorbC2fG?jJQ^R5@53t@+_D3yqX_Q%eCdQTc;n3V4xZ4YgjP1r(>Z6cKtLJ% z{Nkk?7koV+1wUY_*5(-z=Fo%1>O_aYs^V(}tGIkEn=55uyU?Ls>-b{NB)JEb?8zS% zgW?4C*DHxai#)OX&tGH9t>Z;HvY7FvlcOodsS_2;O~xAN)L~QQxF2EzIacM3&j)S; zET$OUahox%I3i&M&b{}$0}o)N$=Cw+0o*%rA#_4;zcbK{%jxUD6E+U6X3 z?ZaWU5tc1J#%lInexGa17OHJz<84I)XpX{b#>T@xVB zrQyPjzY0_*267iv66^83+>bS!h~kRG5U6}-wvPKTGkl0?=y5yw`6c!CRWr(Ph$%EZ ziXHxC`zhu_L?RqiS}_wsbs&k-ai!^J1d#!y8aEa%sMhn&&os}p@(b?mutE5A{#zT= zo0kN`9J_PoS;+U}gHXMwY`kw&^CLB_LG5(Sp`kO;!|*zv4iYH|+d>Sbslp2tD;-+r zBZfXQCq8{0j*jCsUP&u3tgv91`X3IVfuT{;uq#Fm<;Y0rt$4FmyW?}&gMYgP+|+U8 zwA>N3awVS5iP}|oH994Gq&!d(ytH6nhxe;(ZM6vk+gw~*%JI0_oh(G^Yaa$uLr{=T zW#tTGF!95fl|e@FRcz(z9OkrPP5tKS9@y|FbWi z4^u^&yK>nN!%FO0>tP9Pss9d*dWv&q1tp_t47Q@b6%caaU6Ln)wR1l^eQb)fM zK4lMRDVeefzShZt6gs|i12b_t-T*UcnF(rvw-z-Ukk(BA;1nTBO&|Xl#&b9PyamAh zO*0!)4Z5vdv>}09_@obc!H(;ZBDy%C#7iSr(I2Yd53MAJ;*~&xFd58HnnlXlS&UdA zBGUi)>}92M0Q1>R%)AC3#gJM&S;MHUbqLth{A&IgpfyWB*rpOaG`U%Jmc6gIy9nHq zCj`jyRKC#t;T0Z3MoF1H35e3t?$G}B0)Ifv%^;(V5mGQNx}lnoA7B@G^wbyb#GVHE zHiV$)DQrB=r~$YVxqzzeW4>x6z01h?O8>o2?g6v>V9iDL8sGN<%@P_?&NMqz&cgl; z(mFnmz3yS$aBcJV_c~S+Qs(T2AIPil$%S+;ZkYGNZm0`Mgw;4WuQbI8$n@!#@fKz? z)kF*VmaJ{LkQiwiomYRM=<1lf(7FS#xcIW5GNSXD+T_<8Lk!aM0!M=5g&Rb4z$1qt z%*Q`*WFDUw^`$qwx<;#Q&NjJw8EJS;(2V+rl~CNYl-R`+M0N1z^}`C-S}};cM*8iJA;)z3nBM*jW>&cv%l) zwOP}pUy`^$*qJ?Y4a|lkA{J?FncN*3%Z^8^puPmltGa=|0%)A0Gcz;GZS==g{( zBbFQ$C5rTMz&84WL~pa)(E+pMxoWb01nRIpAU~?5)#pC29=kp6FUK1GjIUz9W70ZZ zf0ZPVahsh=)jQZ24ZfjTS?l46GJ+>ON%d%{DNUM6Fzg}qVHIgw9B2-9%XyUPh@)|_V6k4w)SjTiw2Zh6A-Vl z3DoBpTB(ngNJ&5R0o1-wGwS<{%opjbV-JRYPKS5vix!IDsDog2nsn(EnaGq}hJReL zV^4O3iG1SIP>{t5!E6}=sM{KyFoof?{Y+!$V1uN41q-z2#89KXW-v(3nmjb*i`<`_ zi5eyxtsAqa^M`;|Z!V0(zoCu}{Yxg48NbQw=1C>biE`<(p{Oj_h3A&mb9Jz5KcB{J z&zcNZy(y&tKBgNDyIBUZ`*yN&E-0Np6$qN48g*&OPs&jV>3{VxZou4iEr8kAh8))u zt)Tz0+^sdb_g9-4nk-3XDS#Zg!VFB#Tfz=fbX)dwmWh!uCq1 zeBjTckWZgV-e+gdPOarGjsDwDjHUb~mF(r_dVkF|df&#Xsi)FFL!wmQo($?qLJS@> zV_aWpsc}yUiru`)&g1v^4HPxj3KG3(Y`z2wDtpkNs4)AwaB-oFN&PAVhDR;iNs23Wy-KlOPMo=G*xC}ZvI^`^ z=HmgdwOa{&u38giUfv!nMJpALoKv5H9W@O6D3?* zSeSLDJBpF+SRVqNGMuA%_R!ieO~>GxyKvjRCb!`S*j-Bdc*WR6*3kw;)4GZGS=ayJ zyWpJstHmP#y zJ>D#N`?jeKF!k zcA~oS8`0?PiO%3<;7_gbp8m0nxKm5~o$wmrmPxQRnRPuQa$c7c`EXATw9dl{G8$UwFMU1?!Xa9X2ua3N1vRd{mc7W5d)-pqxP%^*`!^!VdQtExP^#5>XPbkGou3FXjF+0I69 zsRZYh6zaU&qF;q|{5VX>4%I&NqJNVVlAznK!QE{fxAX|*dZ1oBVP@JEFvRd{C%sx+ zYVqRRJB+$p6gUl*0@ zr71g$`3u;D8}4Tz`oi~Zw~i4Ife#z{+L|wIwV!KRO2~gUvy}=t$dl24;51N`3XHs^ zxV2x~dJaBff}^peIxNm_w(WA?N)E9K32KBH16@CR_yYLh0tmz!A}DDVDQxEo1=)Pw zujp}hpN}Q{-!z2j=6|O|BKIr#x;+lIoA4oiue?1f=JOGrwU5JC35zw0;Zy*v zTQQS6!dg1bM>Ls;RUAIehu}#_;cm)r=|zqvt8$QsFc3O=1t=VN$D&l9LxYMe>8Y{x zkYr7;3K?5O`B01_K5Qsrkc1z(+sg?FnAsJalX2zAUxPp|@aQ+s5N(50i3}=S_ZDrw zCc5csn+-|!$fI7k>PZTaOS7pIVLGD*NsQ_gQQA&xe-+w}MiBwJZbNhMJy2StXzyi( zF*M@v7FB7EWL6Xr+ipZiYJ(uMDu$?i%eINs%rsdKN<0XjZlMuViIZCmn$|usq~Qbb zO`m#l$%XZ6GRLn60S#Yan&8qNQANmKR?k^O^w?m1qt6!OfR7DIjP&TygMcc zAdFqt1b2HYu9T*g0Ph=?$!mK)gWSYZTfvBLD znG@f6tX8Rdcnk1D`_)=jAO-{*!PkNzA^0}s&Y@Y4+*-r_N~cnXMwt|=3^fYNznCd)=I``YqLQ$}G zga=fVMRp;TsnYBj*+i5AHK}1_ET~vdY@)Y}QQ_dMRYH><9SDKyG&W2~wT1^>9pZ@} z!?}aO;z>!_--d$ro2~dFw6ukiAXyNvuiTWxM?*pl->*wr@(8|HLR1KxT=FQ8J+o+{ zkB_%>4~O%3$_qJzkhfB|YA{wmqXYy_PGBBta5G}`AxiDx2N*72L&_0N4)%$(3@B21 zKF=IDer?Lgicm61?j6H0H_kTAt=6|)=h{QhBO0XhID#5|MK@K6%*8mjY*B_8ief+` zvDH6L~W4{Kt~)h1AgCQjN*gQx2w>+caBpW-D_$uaP#wRP;+o# zObcn%onG0XIp*ks>{bo2Z+gBZvww4ZFga$YLrmD3&2ZHU+}ajwbwwdx5q3by&JSd) ze$1%F1mv-yf6;nLQ}`FWrE2FG(jF8dt(Q4heM1hnj+ z72=gL{E)APD7EOAjy&9z%x!H0D7t|K1ctscpm6D?Ae~n+I|yZGxR8z8$lNLJdrZd+#GC;QT-eKN#Zl?Zn;Ic{}u8gGV6z z?VY!uqw_X87#*Wq;>e9%0?wOz!JJZKbr|Q$b$(n~r=d#+n@^jn; zaU@*n(h445Fq=p91w0z98V0|(xy!^AxuW$gk>i(LJ<2>ezv4=6?G}w1ipg{CjnHPZ z$wpU=wjABZ_1wVgrKc>GOhu6-=g-r zkRtn-b!c4B>=HF7KU*j_Myd6Kfenc$85Q2J53wyWXJ#th!`OCWghx-i4Im5^sbo#w zfB>}9*(beVh46o|*GB5=DHGK-=K_ed1bx%udQG6nR)`QwBnz0xmu_1+|E-x$(<;iQ zV3;lx#;x!S>Og^R3e(}I@IzFnzftB(Ql!s>4`G?C3tSVfx^}8jn_tfT$bvlsQkI?i zsTxpH>{qO)Ehi9J5!LH)C21BGs^CSsQeEWh*zB=DDW6e!W4Sf{ad7WdKO512cF1N(>tlnG4FmwxofPuKE3$r|F` zTqPhTav2529G{)4Sk1N76tN}vp^#!4amGs6o>8|`#On6G6E~H zAu+7f#lAt&$5491-`1f3B*?|v7Yg2)JbM+E$p=D@TY9QdC3>mO$_ zKd)Fe#$l;{V6XsTLK~6$OhSMe&vBK*w`=xK=O?gLQ!OLOsAd8V0GDqpq{i2=-YWF} za1vKQ)l$MXM^Zkpz|Y`VfgIGvu)8zOG;1jA(T1P|tLfVpLh3L4^t<>(tY=Q> zmE<{TpnR6ufdUNNp@QMXoRnN?ddbG*+?-i)paD`(5GY2&a?%LR3-~p(W4aulWv3IO znha|6)@>!vHCR0 zW>idBNG3074z&N)OImI`h5~eA1QxD=6c^@v^4^b+!o%SXgL}dZi5FY(MADBvyt^fQ z1OmklW4KGEX-+lmEspyQ^~FDJYzHidDCWbSu}+kf6_4QsfE&!*>;pWNHtX8@9x*%{ zWHnk8^4bE{xz>CcCRRdC

    `((ANIaY=v4H~+gqtWO^40d%z$9InX3Qof3+)Ugj;m_;T+ zJw2-8&sTFKusqMZk&?7(Z5e+b?2tB;*^!$vag=#aY@t>}PzIw~X*6Ufq^@|Uh5wdg#-MMW@HjcpAB&>Dw1{k@UlGm^|lHE6%b$Vtvl3~#nCAIzLv=c6Z9WP;i5_CDW0+&RWb9}#A3$OYR)s>F?qgqo8! zW@E7aXiIJA4Smb=vvAlor~V*PI#gonaxVIW4CC*_a!F40fd%avww#$$aE27%HuP;vgXYid(tx2^4rV( z>wzk@ECWHuW3~(%g<(q^+tm~???8c9#yB_K`W=Sugi+1ZS zG+sm4;$GcF7!$;aI_L9O&9TE*H|(#Q9j!rKw^aK#AU+>eu(eeX3v6}sy)gg<#YdBL zJdNurvxX0AL}zh&3z(Gax91_y+4BYnGGFUF4#)n{?g!UV@ergY_*Gl5|`T*@^_bYbj zhc4mO!fv7O;NX}0Ag!wT7!(67qfXeHN76X^Z#}krb#6Ms8>zISrNr#j!Q-@fO1?7e znY}D{YS$zfTNveR*FHWJRH3X4`lmY1G}Tj^(P1-_W41HD#VIbzzyjVgiVcnVv`flI zzK|A_Cb0)X8hgP0OcBeM*e5>RbVNbgt~;hy|MK)$7B`8$B_eVKv2c&p0t}VY7!Lnu zg?a$+L`xguon4iQTKb2~vXKI$^QkDId#K%S%N>~XBy>k1)M^Zf?UedS<|{TMu9VpG zHX#-t8b43A!06_aqoGGe)^-R4an=Tg&7&noC78J!ivn&rz%-b!3>!e$Zp;>6)1JJP zqjLxEn%7=YnJeeu_eZn{+INR#*mb-l>vqAFD>rLc$8l+qdP-U+vujU$tkF&^-6ymn zeIEDBk~crP|!s-^1!`mKXdYnzA{D-5 z0}j2{#Y!>6onHH@RoC9}*}r1Llol}TcnLT#5zkY>S~ia=m3#TdNXl5Ct?P7fR}CIf zAYflGxk$aWEM(fsV7cPy0gT^a#Ixa1H@rAz^m%Fy!f~Fr$-2I>qMQ^I>O^N*h<@g} zPq@Ai^kx^?u3_{nTXKQ@prlPhh@ITjv(tw=N|`MBxFwC+l38=$_Oh30+Y=Cxxfn zlEFC~GGmN-?hBXBgmni+8+9oG!duDpW817sSJ36RYTh;BrghEJHG#$$_{rBYPSF#V z@J4x}{#Hpd@=z`{oFCJ)X%JO{Ly6A$20P0r=Zr%oLdLNClQnr1g%&9YmXaAiO9eV~s;N`U;v9O;)xr+i=auhV6p$LK@adsw#i{_ohtXWYbu60xW$8Sh`* zR3DS6^FD$PcIMv3hD}V=Og_>Q3eJXUr$4KSk!?51GCd*e@=Lu6-7&w86O-=V3QCKP zfTl2LdEZ0yC&fouHI9ZcT3w2_3U|b#9c`H}Hb67f2@N|aG9pzAAa=}~{1)nS8u&`1 zaHTSw$^*?H*%`k+_G%-8W}r~#PwlF{qBkDXdq5~7icF@9!AWLI%!TBumrbSM%Deb) z0IHYAm-nD;(8B?y_rI)L4g2mbA9pSadkw8XQPQz^zdr_SHl$Gx%h&sPtfbIKPLwMP z8^t9{^uNlJNJL!GA7u4M68mu}f(iVsNz52c8ldi(U|)I8Rs{{{B#W z$5!a|5lu(I%{y_e zg$B5{>nDV?9cH?vc*r5+1NEh*ciCNw5gTElq;Qf(v)CoCT})nCq8VkCxSWB zXz~Ol!<0y!5swY??r&xoJoHP=OIFttec)-4Fe#$;*liSI-GMy0CMZog2cK`R1Blj2 zrupj&$ko%4I(RIZ@{CBxs?ZyNQL}`?3}q4rV#J`6s)@!*dGv%1a-J%cB(-tjDlEav zKxBWoP9zcV9<@GaEQ?i0@G6AQnl)&VbZDX$FT3VG%?1}@6d7o6>gA5`XVQNY!-6DG z-r%}CCaScbtygaXjwzS*9$q-z0j#mapoyA|5qVtc#7IqZI`ny^S>%^tCo_`pwPJdl z*aaWCBneJIwgQF=q$r;su^sL38u&~PWrXyHFgJJme%gUo1eHA6DdA1D>rqU80zw;m zBN#^b$iX;~nqMTp_k+bwBHp-PAMC%Jlg?H@no}w0kl(nBeqLbkz8!5}r#r9N1m7>? zzs*lOuUiXOUbtd+ZhAN{ztO($Twbp2p1yy+a(O>T(coHLalh>Ea%JImzwUW0qS>;5 zy?CH;_z@in+ZoG0pw|_SIC3Ke{~=MgA0(wPkLRpUsaP;LBFwHWmP(E6s|_uG9o-!L zv%ax=vLZNGdRSWa#I}~8by43`_&y&eeKdA&b7c5P3UohF@pJWqY4rPag6%#XfuVC{ z-aKNvEZ^D>D`jl#@8vPsc*n$0qnz5jEMLt>$wzi;6SD^$!Kdm-={(~#KBh=}7)l4U zNrJ3#kdqzv6DDF%7_vs4)_ri=E7!i?5)b~u)<{)xYG&R-JL#ThieZJ?1;M{%1wr6Z zvR-5N_?+flO2klSra&kw7GP?Z7~r>7#yz2C7vDqQFo)!3DI}kXnzFHCi1SIEeVK_efuhg$JnOEZ1e_DALut6XLei_Y;mk9G2~K!DLl z?)b?X2Jie*!KY=(|K>fS zZv4=o;9>E~T6h_=Il{cmYScsPaUt4RT}g}<+`4l3AT5V>D4FQ9%GG!;)AAq^vk7c%p39x)PkKr_3!#R?;CRJaK)INf7C-qh-WBPE?)9 zW7Pl6rh5(Z@-Ni$1I^Pj^zP?Jxi<_(^wSk~ghOdVW%>4I&AlcD0;RUQ6rp}~T$#}J zf*V&xNcg-4e8&Z3bd0X8fO6Q6Excmc^1i5GKCm%i!YR0pM6Fi}U#ZE7W^;@IfV5UA zbF(Wpp3AweoE1I|OQqFbaLZwzP1mFI)fA}hL8PX}{Y{e%;Ia}UuY5shZ5y0QRPm^4 zSn7R5(U9P)D`{C6y;>hq359tJJ>#+ppCU>A^3?Ed5^Fi?)F4|UkjYJU90SR#qfW3! z@w^OPl5SKbW2|pU@0cNhhT(|}A*wQ33;h~$raYCBOC$k8FtMrF?THlwHq;iW?f8LE zQ#vw%W1ts0ieQa3K;#6z3$}j8$g%$t5&Pt#qzA!pmu*QxBdvcE7namY@EG_tTB5P(*WhB@XO0{7J3afmL%T*{(Rk(|T5k4qSu?*u^sXIkwsfizr6*aTGqjnE zTlMSQv}FeJ01r3&(d#2^lEBSPg1Z&T$rs}IFp)bq zO=4=^a4`8R>IHWe&gB2n0{f@zVMq_J)ZQsOo}7Yv!^jH)?jY{E`3Bl~dA571P#@2> z9j6X;aufh_i2G2|@5)e~Jb^Gnn9wm6A>!mN|Dp2{e?|Nf2+WDa{=q6Wd9BL6`l4B;6IR^^qiJf2k-4M`f`Kb6 zE_TG%@HB@wHgzFg(a8aI+;znag25Fp;h3_BK1NZnti_ z$)EC*wU|SLBE`gm%N&5-0F{lluLNVu&8{R!9SOT3k~y%oLmQStFaN4^Kv~<4%v%YY zdO!r*v|2HH1y~`G$PVGsI!3;XSr|&yRI8R1dp|vH>$PegnLMdwh5B!|Q~nRB_UU#`Gb?G&h+~uGs#D+En35ShKu(&dPEiEix>|3d@ zM))pb$htq(+>hQRir&Rpcr_hG57o7}JCGhbh)z2ghUqB}h|uJ=H>8KOg`wbaMNV=? z^9Ih?S>(zMB_JK}eLUNSxY<_l7Jcc;G>p8c@}BD#U>qIKpIAXPTg#)d7Gj3c{*GrW9A7SsTY-x^%t0 zvdr-`-TlQ+{Y;PdbV>-llRM(~{NM!cdF-=9fq$7%@+14(J#o{Hn&ORNm0*0t(9n}o zf#6y=Vbbabpptw$IYYa7o=5Nf=)Z)J=n|ScXGMq02@_{5H%u_k#!`(1h57LO8uonb zI9#bO8O4B=%K7->aUWBIeJT12i>*k(*pae9GpS9&-SW|*GqSi!!q@?eASJ$d&i9q{ zF=Yi^$;6^6#fdu_c|;Gt>Gbf=oFL_Y)S7g8GpMr{qq%yo%Z4 z3QWig5lJEi?H8rL2x(mk@_Aq{<3gO5axNTCHVK8kYsj15!u0vv;+chK3Jg>0eAh*0 zu;OGfQr+>q>?Z)MkgZ%0$*7DR!m|;PU@9+-nVjG9^~dEyoSlI z`Tn!}v+-j;?HfEVZ@0GEZ;hHzdCnqQ-R~8w6O{=K)n>WcCVuk{aS(Zm5V2dc1CrFr zE9{9^Ir*SR=ZgRC z@^Oa;L$A2PoCQFq!=k`8)!{*0muvAMTrer@TM*$inKy7P1#OdEcFX7mGoA>7SU_Db z&U(AKCbJcBM>oBE`(AsPx=SRudeOfU?avPYDarF|seH{p^w;iU@HU!4ju+(f-;XrEjE^jQ%^)tj{O8dFlY z>ija#WrJx#Awgu7>$5}^aj}Y{MJUF?riVW!RNm=d%u%l$mFm=f&)GnetpIiu_A-3b zWGU1KLeVwJ+enO&W_8;?#9~Hy#@Oh+eWId{uNMLURjJ4q;G_+}IDV+8Mzx(ad;KX%+q!3Il-kAIYu?c;QW~aHEfU3m&4)V^b9)X0Zlz$5aXHS3V7hOUBQ3P zIoXGTZr5*jeUJNzWU-aHG2ryhbjIYc>%n6w76qW+ZX_!dv5Yw0=ZRk!$XDon!;$uy zY_vK_oc1G+U1~-SEV^z~&^ZY1f3O2UKajN!jIk%N#4@4{jy8DjagC>w$j%T33P8pi z3dLMyC|PzLjOFO7IUZYb-vq9u4_J`ng;mY*+k6^AiQC%ZWoSszJZoxnSjyFoV`Lpq zxSCChZXo=H$wsNCNNY9)XAS3oTB0&W4tU3QJJBTE^QT_kej~mjiE;J*&VD~{;?<`< zpFc}M>+qK82CMpS{DaBS^HAKV)iWe`t9*#)$$Zl*v?-^RZ*W^|wBJ)*smS)dH^AQN ziH#M#3Zp568>S3whNX>}1o`8zSJ?wY0n3o_FQiWJe;e= zE$rUFM`if~mNL!~4fD>K51gk>e->0-l31kYZe2CRXSb7^Qp`q}bwmKFP$AW~o$SQn zIS2R$E&7h0s93Or`O3!Daf&xanUnQB|!4~s}3=JO)evVw{H(qgC{8JDZ&%NQ>|xDsSVU>XkH z60&DMRxgtz6fE?8UfNVYj2gWUlC5211!GTo35azqr4%*h^u|#7l>TL~-%-UkG9xJ> z0M&(d{Zj~>+>?Kk#8$aB^$osagrLRhqm3GkF)%?`Cq}9poeH)rQ6d&bEvEooN)w3! zSNSM#cQA}Ub+J_LeMH!r$l}3#&RTvqdV>i9;)!zbM>G=>BRqk<@qYc)~7L6(!n`a%sg1woi z0t0|eATJFGNQKI1OEf2D5j#$z5hRlGgh{Gw>vvm+_xf6pAn0-ajh$)7?$m3G-Y)9U z!k2btc!3`a1P5qhx>>zK0K6OFq!;~@$Egohk(o6L3q=sY_?=LCBgARFnIdu}(BLE- zSBVTbX7kzs8`1t`rjw3S*{WJ>PyXi&EFc^Ap54cYuRtmH)5D=OvoxW29$D%gmiN}n zhQ%;Stud2a{Bu7l&v4(KRy*6@G7wB6JOW6SE_uvot#-7nwg8(M9XN@}2ALxg(gjES zj=PYLgx-O=zY!oNJXZCtY=oKtoGo_x>czskt&of}d03V^W}uG)BRYn_BjKzpEl>KZ zlK`Lx#K4&7T5hnXS71{nzA1ul{WAz*w_2Az= zkEuP3D9RU?;A&AXAU!@5?Yhmz5L(SkJ5RX$K#LM7(G7$q+>_@DCv0{47!J)Pal9q9d;Y4-@yUJLxf(M!6+1;XRix0QIotH*qbJXR$I~kq zAzGtL^0)>i9u+_>dc3P>*%Qkzo_0zUOUG)tQh`Zp;?LEl(S$4-Xzy>8P(kd>oFs+3 z!V0P6v>eYJg|EWA){0Qf5v6F%(kuQ@lueR5dH~nDOMJXjz?P^$Ib+ovhwG&$JP9D~ z?Bj793ip>n-If!iwChyodu(Pf^S6tBD{#^9`2UIfA{R{pa8AsWjL1H^*oiVijIw}6 zuaY@C%+Oz5;ICEf&cfS}a)^JJ;^(c+5R8-mC+T~{h(2B61Ve<3lSC-p z{~Yp-Y_;aIV+{jKYXUp$KsYFdUMH)@0U=ek0Vcj|@}}Rl{Z*9X#iktnN29+B$ShS9 zCDzcLNMOm>sa=KTeG+}Fb%YXKeGqnitu|U8iyaoHJ)4%p*08EQ>s&EH?@javfQcwu z4gN3YB`t*yyruyt?2JoPlZ+TgK9YT| zl(VK8OV7Kj;a3If^IHNM&_0{65Gk^S*^o|xVoYT!zM#5ccRqPv7_or!$0W7Tcz4x( zu8BNBL)Zrgxfd>=r_##RJWK-CgY1e>mQVB5w12N!Zj>NP=&S#{&;>aufQmnT$wc(f zIn=O!^Of~Y`5)#ha8Gd0!EW0SPu<4uufguX!R}GOz1-hv%(Y6%I+ztNtNFF~94iAE z(~}6TUcGQu;DH!J{3Q4KPJtV*s(+#N26OikQUKi}V z;0vQD%=^kahDxSH2iJkdkw3*iKUTc94DH4F)7sPYX#?hKusn4#|nKBgvSCX$#))fd%&fZD5GkZu)+>&q}U*EkOTuV$O@t6 z%z{>CE&#zyI&I4v&A;Tx6867oVS@tTQmfZc<|zni(-3ik1#NPQl{o6~E34*fBdO3~ zM()*MuVw%WuG-o0bNCMddMVLRe48%jrxOQU8>>Q%))%B=KKRb#m_4ua}Xs#u0bBFY!NJ3>4&A) z*4B~3e0`xVfFT$M|Kd_v_>nC1qLGX^##UIPyk-`e!TfO2(_lF}+Y3az(=;V1*8XOi zvk(b8NMAAUUB9^Z^EBQ`99{h@xUNwSF4N^KSY6S@0BA?-zp$f?g zn^B^}V3A-q+3=OZYM!qg0o1WRY;qS6Dz6Z$jUDVepVcj@lL%aOO`tc?THw0-NSl59i#la*&8;HU>STc#ox6ue@WH;C^!t>k9y@0)OLOx!Dd-w`7U(zQz}cIDDvGsZ zmLRwADyGg;7R|y?U|X9}JTEOp?S}o0<5ey^;P-HH^cz+Z9aWWj=+1-AZ`KAvEL^G} zH|>m<9=kKVJ88=V@|@?)d7vK^Upjb+QcGuCsDo59!>V&dCEhufzQX44_pUHD2;d1NXH=J|pr)Q?uuKq9{X_#PASGd){H|E7r&m9Ak*lFIr znoy|nHgsXgYzy*D(5v|@=5Lf2!z`>Ta3qZo_smHE&>Q++r_MBF)$_qL_Kz1Z>S9^7 ze_N5YhyVSF1KM#B#MgVKsCI4!3X%;k#BSPW$PKH>j*cZjjHzZ?<;cCF!|%q)=BYf) zddyRL#)4evDoY|)skF<|d! zNn=#t>)(?!a=Rm9y|_li6GfhdKl@W2LhT(MMk0z${Ej5}hf-W>(sPB7^+Dd%^O$2* zO(3od^ehoMdUs&`BL+Mu1W>9NC*d;;r+%xwE$94>^hKm+y_&4}OUw<^t*X58b??mi z-VXw_o->lX?P|P&&MTfNOP$54_c;pA?QXl{9mmXoX!iozf4W`#Bk$YRubcOK-jERw zqd?XDNWhyn=q^e^7hkC)-IM%pk=#YwZpDo3E6&%gG*}<>6`)AvDyux?j+eo%z@oM< zP+31M>`35`D|Umg5Ucix|5OD!gx{f$fWO`Y?0~i!-?FaFVp-|zb4gioph-gegwQDv#7! zMx&bpZXo!l&TA4m;w9a4gXv0bCW5Cgh;ixAqzlxZoF1LMSjn0|EfT*RT)UxJ}ZBpQ-2(j=U+2hut}& zc$QJ52$(UOAsU0j1c0!_6@8y~NSKWU%yq?I|UPJkh}F>H;>5ezPcq>a*fE(X#nGv_iw_P{i86SN`a@L@o+#=<9wT za_kthn3tn>b0wq!fC27&WAJ|NP1YSy;4+zanIolSb;hy6D&#GpXsk;kK-Ril=y-!u z^{DAnZDw3~;wg=zK3Gt#{!bTYHh*MbX=$S`5GRddcJ7JIDO9o?Kp#*Y%VB~tHyDr< z)2sATsofAypnRM!k+w!%4xSFQrf#NKp4zmkGl}WZ{9N%(IfZ|a@9DoymN1I~&f@Md zLLv;C?K-ypD0}H=2(Ma*{#K`ri-(CSDloLFtDQKXkFc)2G_8zORfv^o15Ch0%ON^6 zif&2WV*u}`^-Sm}&!TLqSr$y4ta0o}b5h8@p+8F|f0|}Z{)e!83eqeH*9Bd+ZQHhO z+qT`MF59;Gmu=g&ZKDfaRnr?YvCqVgI1%f9-K@wJncwri8NOv{i`Ela3&u#$@+0pg zWgW&#k%oI%j_M)NF+&}Iz3N+VsqgA)u&KTMQ6v!=EM>~iptLA_`1bKU^7MdpGPo)s zXTE&HBE<1_5QPFAomaTk$ z-*7w zI^RVoyI5=xP|d^eo8`=p;Uyiw*)*Mn2>p7V@Gd&$esG>YTR{-bf0z@}W<0_XSe!uN zx@C7wl|iqC;^FTFnK%mdVQupluQ{ppXJEEpH1@1$IPrM(!f+LY7!S?#Y<4PL4#DtR zNtTBM`*zDs4|2I24@YHsDo_dtaay2;gmKo3u0skl5z!+ z_`@uH@1k*@CNgS!@1z{}$aT}yT|#+j1!^9yBE$;B0fJ`ID-si@R#ugfO;&+EVj&63 ze^?OCpyKPtP@I$7+v#)`pwZ3-iNB*XC`rbb5w4oYN2f(qrVJQRD0J3idH{~7AYAHU zON1SWGj{)t=1k|VgX4z^ppg&Yjj3me#$N@&33-WL6pFSvxubcl<=xQDH#N|iQTSLA z7E~5qrm42SMeW3{ay3Jl{3$^N;N+vrtXR9w^0uOrlW$Z^s%N|62t)xk*c8s&GEYy@ z0Q6!@!YhMtW46I0_Aiq7^xR)9@CaD(^g?*i2NLeQ#}<^zqBfrGL*XTEiD=3cS_EH9 z6cJ6oKA_=xbEUZG29kRmeJ7A|8`Fr_ubhiLb`P@cGBn%8pvH_33u}kHF+JhDfJi%G zu&P4B)VsZyZyU$eSqA1jbTr!|#H6*~Rm z+(IVR7}8mQWfLWC0Y(D&xw-$A^Hv&-OdvM{fBNFrosnerqbX@NLveBV_B8+xeNrns zTWd08$FVXCTb|zesl{yJ)l+kq9-=ZX-*E9p1|L%X+KP>Fwi(Dh`9hR8`RemzCPjM5 zw#v)aEEf;ia(djXfak$;x3-RmhQc3>9zpZ6N>foQ>#hEYoCUw04(_9US}8Qk-XS^X z1;G)Hl#1sD`N(hjEmJ~4p1nmQkNp{A(cAu7yF_@Bk$Pw)H>(V^xvA9`J9M5jW34qXM|R20rQEa*G4~78VqK8E|G$k?Aq(j@6&_-NHiltULX7m zABgrL931>R)kEBS*XT$BFcH*ynf*;I3hDV3u~PL>2>d3h{^uV5b9QT;5QA?oPEtJQ zN-X5&9o@LVX6ow5+{2GM0*KG-Bi48e8_YsaZBM{=y!S0=WA~>14(l6Qa+VkC?c+ey zJ;ZKe^Wd6O=}3RxQQMf?vCVA-TottJWFhxq2?4A2f>BO!jOaNxO~K|w28{`8}?vukJZCJK8zm>#PW z+5H)&VSn9>Sn|c9H;|$4K&!F!pPS9BFaQ0&ulTM}&C7NcM+tFmrH6uQw!0gZ*Kj}h zEY+pNHQlephMe0dP;iD0g}|C)Z_>+9cL^hWj0|vKhyNl$;dJXir&T=%yWdWZKPHZM zNZ&twm#bV&1K+cx^X3&b#!r%Klf}3r#Js2p5q+Q%w6&x zP8wO#lI+Afko87(+)@KQblE`$^D$RTHyCVzuDV$Iz8732=tkY}3JEja|FEMMtHByD zq5>B(R*9Js{<^e#$?BU&;M?Df4Fk{KndGQMWyZkN7jg*R-;?I!SNr1qN}Wi z7`GZR>tus!)heQ)xhymoPeS)%{m%cK61DkP{5XdhGD{&GBih&WQ(NThz{()BTjZ(3>Gq5ja zN%zRsIpe0uc@*{N5ahw)C*beEtDpJ`u0urpvtl^)!Xy*Ed?EqAtZ)CX%GOF#s(;xY zpt~-ocpQP!JDnLIDr8`K+r*#}!N7(HQN*Q4`j>uZ#r4g!+A0W?THh-=gjHD2rcF5?w|6ff8I?RX1yF z!T2@pV{l(GuFasl?F7^wketH&(Wq$d0xu-PB3$cpA^*#rzTCGl>Q}3VDJyCPB;;V) z@1k!A>@d82$Ns-o!DU?Nr5DUXa#03Fe) z7|^XlDXxs)b|KqNmleQ!IikMxfiMK$m6E;VeEx~{E|$5wZFG&sUNbvOPS!J9+s31w zwNMe5o&xz8uSE5XD>e?ouo>m^cQL;z$1Bz^ZR+7Z$6HThMj9~yus(1$_yaaQJ|Y~)pfp?kVX(|@ZONmmP(R%!RA0Kc<_Cvu#~sY9=YlBzrMj^WUyN%9#V|7J zV_iNrbwNLDZPeT#sDaf%fgOa@xsO?%Je6SQh#7~KR^BQP6Hk#HIwUqrp^Kg{s>sC* z9+2GFPw`1is{R|r6fEm{Pw;4ngJx2e?gtYD%^U^SeJ_VV?{UjZ5sAMAOM!W7Z+nbh zvUl$1*~myl<%M+b7kAK``YvC8cO5`Q#xSo}HQU4VrDI12>}80+Nc<*EEd2YBbwhYg zrF=mA<|xpO`zs9PEm&xO~B-eK57rW=*9s1n|3YTGpAd+WZ?!-sw8i;41a{m3ZSoJCKbc#~a}&oj-%)nhuHMBudJya27VLX*;#uBgZ(IsL0AI8DA4SPznBaMxZpcKvhUpi6RA zP*-oB7dmrp)gph$EJk74|c)J|EU*OP}Z4g;~$pu3SaFiyOcykL*qv-AnI~&)6qS`tBz{ znD6>RK*MtHc2;zHtLdA-Wb~kL2+bZ9J2`VrJ{obB>e#|GfdFUw0k5B+zeH}N+285o zLfI`}Dp+U`{bg!Eig;ZlIOiF9v2b+xTt;M@mD{PZmF7opJZOwgo1QX4{qppa72|8P z^J&G6=Fztm1l3E`WZt8mNFUU%jyN43PnI`()Hd&g1tVd*ZH2ze#0^rrF@+YJ8ygj- zPL+tJ_d&d=50xHufL&A_(bZ!V{nOr5S%PPY1Wb@)TFv!F;ai9tQWE?yG=&w6I}(*2 z)OHuOd=AfS4!ppuhr8pZqw5jF3BGQEyGv}g;O~f)?l0i^3q5}A7Pj#(XFKeS|LMDV5xgmb6 zGb+96E{bNxrw7kr<^CvLzUseHK6RCLC(hUv+C{4i3MJwxOKbRN;%s0wgf?wK^{?C0 z>kNP7G^vmz;HXfoQ<7c`Zj~4t)40Q?P^9izadjFJBb0$Cxmt>baZHuLFgH|WMrwk3 z@vA{sxc!WP>huRDos+J1o&LBW1!_jsEP_WD(rVK-u5F9%p>nYupKR}Di(YN{b>?ma zsw~AeIyQ}oEH})Z*U&T`+O+Upjj%MD>}y2)*0403u4*KCjPNuX!5NIe)v8mSyLFPl zIs0oA`xOm(jOse)qSD3K84P)fs5B0L)g$nlBM1t(OSQU)`R`LWUyf+PFSs&z-|rK6 zytij|*Zn0{lcu5Lz&VSnx2_Dq`*$!>sF#L(YOwq~)r~#6pgD`Ck6&_RZ+~&IWbQjy zU7-d1Vkq9+vK0PRnqoaza~AJ6ZM?o$Q=t0_#2bU>cX8D1yXulg*P5HE!U}xBF{rgN zKF{f68{S_?^K8L0zxg(DmzjR>#<0J+QBvPBZD)wooZ{*Ob|Qim=PK%+3eS{xJX9f6~SFyT3617&tSW2KqO+C0&SRSzRJ4f#t*vbBmOCN?`bR z-N_<24nMTE;NJ+2rbJz7+{q0JiV}jaT`;Er`y{doTGWKoNa4O^p1W10`81*T-wHZ} zpvF&aRM3w02{`FCtc|`YnCb+muv$R5wIPqr-1^W~?XgT?>_z^4c_(q3=$8ap5g2>+d%630Uwj%~|toHm$Y+l_9&U19ir1^d;Lu zK3C^WDle&C=1YtERwIS@0l5-TCff^x;sqMT6nckgwz9ZF?AeRcAkV6cFa1Xx%;~X2 z>tEQT7RUBj8jjFquwQ@vfOgzYXn6+f`UC>l-9MVOFM)o2p;6zpQZ0Fox>ahw=kO_X zr7LY{59yY$s{p`F0lqihQNBC{%Y9H*DV{(t?*t%`yEcDe(8HiMg9Xu8M5oE0rff`( z`SlwRmOf9`=(VIFAyHkT38uVSmMJ!gKe^K5*bVpIF$KP>dfd&EHy=#~(gBA0Yn40Pt8y z0JO@np%0N9HEFYjc55@22uc6*X;aq#uvZ@KCTTSfc#?cz5F0-3rZ@aFr%y)9JPAQ! zF*Edq^4M^VC!zi%1`)VG`KW}l+4Z>@Ff2r>JT|j;tSZtJY*$PPWehr0Rc^(i-&vim zH3ISIs;WjPyCGv0HrQT#%xU!~K)by%IUh=#syAb@xlwNk^id=;h@PHj%EH@YxBqsj z|Cy>lD#Vr72%Uc1QPPX{`u}njMmHY6aNv!JptCyD4?smh`OGRj)K?rIUr!b^xyu1k zkpI57X!f%?VTOf^z-1Za&YqnH`Kw|JRHskriJC-bF_bbE66$cJ#-|lCZ1j#~@LGy0 z0?;Trp@7m$sa36gRLRIcymjd3U*Vc9U3JDSY{Wd*y~bGEl=QN&L76_oB8j4tfpc(! zVzksbzKP~|uAJlwN1m;r6RPr?9M~4-KdiBRRn*wkG+^UI{8;n1!&8lh3!KFJa4wqMRC(jjCX~m zPirDgae~ky6mLQ}sR^+*gh3+pHIzVQ4gVT0x+gW=RJEPgUFGDfGLK{E0#Z=FIC8{| zkg-dc=?u-OK!0M5)2i*zghlxNn}&kuCo_qX&f7|y987fgB=~bs)-)+gd~YhCxNl%h zmG-FYMM2!ZKP9yY4fmt6c|u8&Vp}Rhr;*v}Q>x7RZ~PuOYD$hX_SNAqfP5yfW6-rs?flqN?MqJ`IIc%mk)2-poD4`QI_f*R$NSLY!_>W7 z<>CMN=IKXl>aVNfjlj7#1PQm;<>A+ z8);k-CexiTg*Cp${e77WGgYHm%s-`SJXHuXpsG%HwvaL=VV$f?$|MuQLaqMaupq-lMJ2W{@6E`^h2N`Y{U;*+{$lU{?`$-keXB!vfxWCkThqPKhFR$5MG-( zF3=-P4E6iR6h0VX0`oB-Mc9HS-P%JE8T%3LWN4b?hMok>oKj{*rE&m*21CI5@il+E z>-(-MtKyQEz6@*2k>L}o`vQr}OTc0QblyA%UE$XjJg{v@PiN-Qjp?f;@=6Ia*>PQ6S&`pN-{P8+#vs`SUHYjTCqaXAq#l2x*CagXD= z5(1$z15F(P(~2y>*mRkLN|wso0T^%pha>CXkyx41g<5lo+2JB3Kw1MOl~y7W}H7H)FWcDDP*vRBC`*$ZCWjF|=& zsy6?c7@EPsjC~Umt?BYoJvmDK_KnT5SJhVHG7d@0?J?Wq>(~Xx6olSrs=}J?J6LbB zQNw`h$xi?sdX7Ze>XYsNFUQ0gDOzrD^$MQLnhgg=NuDEN>dw0|5jif?E|VhUm_VNG z@ZM5eY|j488z9^6j*gIdO{UI88zzlvf}@X`fGUjcKQ=O#0J;Tlhg-#Y{%YzG(`m3S)MsnC3eLh?x2!cmI=({lcizrZ927nR~BR zU`lZMsGHH=bb|Xfh=Zw?nhYgFgGffuoLNV5RR7z=?Ht#nlLu(gGgbAR0(12D@m<(o z@EA-~XLViW8)o@(if`%>cSnR&_r9uB)8!tYK#fdi4r4GkSeGgiLGiTne{bNXPT1v3 zlE+jz9RvxM!{1Z9*QJw50(!_ihgj~nrY&3XI=e!bV&zuP@f)WwDSJ!Era~y^gLRiS zvwE(}^X9e9dEm;szd+v_l^TCExPQ1+GbhJAHCKY(hhoqks3XFU&WJ+NV>oF^$I*j` zpENwIw-NlLHA@$Ey2xK4J!&n^*nqmFnUuC?&|@tnp>C9tr#6afE^({&U*PL#7N*y*2x{ZffVSiKs!+H6da=%Brx6mAfBO}&_>mjR=!&;3g-jM$hB|N35WBgT?}OZ z4!#VbuW)c7;H3*8I+a!I`EMddG~>`&lz|L_-amb(uIzmALdS0U@(N%>AP3$N8h#=r z7>jS-0QopxOXl6-OQnk~Gg|a~`*r48g>5>VwIe1CDhGG2<&`6=X}P~y5U5gA)W0H2 z&Vv1V)evcs@u&xcsPG)+fpePYXF5y{WdU%A+2Vj<+9+)v@{p``fy*-aq zFn>UeFJ^Lb_R5l~$Rp#SfdC$y00e;bw;jN(U8X{z`33YG+IdO}Pr*jJ4~`1&D$(*uGzPLaZQ{aPnae_{!>A5+W#7j@SgQ?6|hto-ABahv5oaxy&J5G;T7)*9{QZf6JYHX!(1Am2@t?}uq zhSWNyPPwT|*VaxdAJwjhGE1rD${_fWvZ&|oZn<5ey}O^e z^bfPM;8(o|Ny)F0f9J~FuOEf3&?7$($JGis&y>!gH;_JtN~@PhgM6q>siUeWr7NF4 zLl``zyuPU7$<<(7a_CK=4zQ44gpzezMnske3;V)RHBP_Sz^`nrmOxMPKc;V5KS0X` zTuX9`AXH{=>#1CmY5uVwOEyw7?Kg54AzZr-A#3zj2P2N~j1bljsp=9u3+G7IE8$aJ z?i#=gifJ_bAWa{z(kQjXI<50(Yasz&!|fazyV3Hlt^Yryn59=~YH)uDz&=6EK8U^4y8Hh7x=sakPfCy^Hd99!4Tq^l{&vQoP3t0gU{@!3~dH?e>?47QUA;y zEFqEAPsX^fs)rMqd|ro8yuZBUO_&OR&c(IGyvRd*LMQ2Px~ygTQe#&^fS;&e+w;j@ zqw)tMNu~I9y#8ICZrQ$Y<(rc*tUSZwHs0=3=P z+EzuiT1R|Ecn$N}gZtBK;qw{p0~ziU4=Ub%me^_3m1^AzycI96ZzlK2XasnZG$Rc0Wp7>tIL=Wh(t`AEPc!X53DDSJm7o*;}1=H zAY409C5oWA0&Q242Uk$x1Wo2WVXWb7v}#`ahBdjS7KL+}k>ehj>mq89PB~bVSK693 z4ilxh#1+o}6Nc3Ru_NKNB4jCn+U5{V3AeTyAG_w2G4E%M54+XJfT;|)aHgq%B{|xk zn4Kc?!c%n!)Uq@+81j8Jh)LY`v<&i+MKMG`%$g_cjSe|lH72&a=qm)j1E#W z6niSj{y&r#g2n%)yo3_$>>tusCdZhFGCSzGS~OL+kJY*t?JPy@p5lPfCaTB`c==g3 zUAunkj_vXH_;E5{g)&x29VT*~cIrvYd3z4r+V5tUUA0^Zm}ci@H*BDqjkip*X{kNv z5nBioYZpvG%Ivr9KIkL$!=+k!u;z4z2c#I+|1A@3QQZpFuE=+xf&-MnG-BYfGr`+{X&OrR=G4n}owX#+R6BF$n{ z-l+8R3jsNYgL_eNNQ!{?rDvHCZ zHYJ^C2|)w(xnj@_sCU&0-b_O$h}Sv;M_iG~E7-`CIX@~PG{YWVsosnD)Zk~c402{5 z0k-nTVIx3kX`Exv;7e@aatSPC9Jb?{oAK+|Z(2w#eZ3#?o z7}lNqc@vyr{>Y+jE$s7DRVs!R!wfhLZ`r1=BAYW_>f1&Zt^FF7J?kI!{NKV(6J3H@`+V2@^QjzK&l z70cFi6*2*SPJ@^Il5QJN@LG_Lvl4zSD&uIllTHlxsM|lS!({?r?yQaGh>I6@;XAHm`ztm?9JdEFJfK6C0)xwohotC20YN25 zw{6^_)F;eP_lQNwmt#|qc;uGPvqR6lZ4%s`CYp}H34y&RnDU*sOP!4Z#p`GDb&nti z;~KiLOUlMtWYKe<#ssl}==k~Fx@|-Mhx97__s4=Y`v<*F*-)F#gJ&olgNl@HcY1x< zI=2Ipm~gEr{t7d*Da?~qTQr@6-U`4#IvsMhi;gqO)|RIq6T;o$W(S1!(k&l&^p#@u zWu}nMbxi<(a9>J4pX__+ZV3`@;+S%E7+)~b9vK*(YnM66rnFfckm6&GR84pI+4k}+ zcLdgg29z8-jF8akFk^DO)q4$#!r=c~Y769avg z?J-*vTc62)0;pMjo=?)_`|J$7=#~+}*V2Sl4ZqtAvH+lp=BadepeIMd;f_$wf$LI_ zH9bq9ODvAr_*jkCN10fk_TQe+!oOhKCyKN;@4C{W97|NozUhcLvHQEm&vu_6jmni<0 zJzPNBZI~vaN8KER!nZUCZT;)68e?*;7N=lk*hH{OI=ODx8y&PK>f!0s4N4AyQ6uw5tr7t4GPGTiw9`_qui)x(9^#On&r&!BmA z`ZEK*k3XI%UkvWE?wA@+9o-qIyT!!4<|RNQJP%du=->-2qrL@sJ})cHf85Ii1&fF&dno`RI+YVG$(*vX1%8l* z%#ixV%Ftoy?NH=v^F1xftx5(r*B)L1pNCrw&+%vm&v9<#S`EXJL{~zbc(B)@4W844 zm>&p8C)LhmH=SiUlf09Hn@PzHc#9&w8b9Q~nZ*FBB%sz|ZJbbXyCg8_1YA*s+=&-* zWg$(h7=cipZ`tXN;{-pW_Mc(3ufcuqZ73{dERFk=iY{`zQe&#hAP{$Hi+Jj#&tro# z#%1Y8gUvGv$h?Sf)}ZFjJhRsh<8=R@;!EBN@&6*e0!v6+?d>(iK_uC1aOxgK%zh%d z5Js2q@WNLh@#W=+h?_}3GAmZCE5)qu&I#Q9^vU(H^8%$vBwxtHvj@O~U);k(Jh)Ea z*Zbx`ln^)BxWT$C$ZN^@@;^`mB^=7ftNZ)&&iyUm2j%-DD>iA$7oh*G4DASq0}7^d z2ywH7^)9-&9UzasqXcPG+Xca0(CD2=a;wJe41%DGY;$;HPTZ_u3xBCM2_;}Ks!Cy=d!V!#j({rS;V$zNj3w`1JQkp(T)uzWdnPEUk%0lgB= zs)~SRFPf^<7-jD(V;erXYRyvP3>F%EX-MFlv?J}M2rDadHOkr3yEk z5RuB}5G-lmD&RbgI3A&s9}}NIuo7guiPs?Mol@-U)WrEaCw=}&gryj6_bskkk1;gJ znlw<)@6TukV~QVU;URHZB#;h8%dkAy)_v0@Ej*Xbt~o@w7Tp$Zy0FpEa}o39Kwj+4 zPF!PGN;-HkEjZAkLBN7TClmo?DGoJ>8z=$8X+`{$>!__qQ5>_9X9YPMs1d0U@goc~ z&m&zO3Lv_G11=k%6zo`7(XQl8US~9?`{R4EEO7e(2cD;lI3+%Vn+eg9jh{w(*uuHC zU%}XEo9PueqXKuSM`E0ubk4}L&s;jGvXSkQ98Lcc%u~xaI0}Ckqyq{Svoru>WcH5c zPi@z_?l^nhjRob;#eTjO(IbuRm#N6-zblojvkt2M1k+RiOf}3Z6##uE?;0U#W{oto zhE6yf11oaalrdY=9lfE4P|_~r4uOLcI>l1BpUPuWtG+V&vNs;&vyp(zO?gb!EeYQzL zj^^K$?lwNa6ye1F1CMc4Wdx~rf85HaOO_oRh6D^*T>qxRW;IJ6+A6&(Y@v~WYt3J z?+i!NFmML=#8xg5S>s9;=kuG<;*_J}PlOsfd=P9hD^gayfYl73c4yU$fHr*9Y|!H+ z2Lbnzc`NTVwfr3?NuE);wf=a4jl+O-Pc3G(#}uCXnnX$R?r1yx%tVjkZ7oUrz+l+H z-kHnRXGQP_4?TRot5?zc%ZfY5iR1c=z3>yvN747!gSoAJdkD?hXdS<&C1HjLI$2be;1$27E;{z>&wZ4Vy(VI3XR+-S>~lzDHpjQf$^WB-&jE3 z#GSk!k-jH**U983LuNV@dgvt2YnmaUQKWUX=>jh=WFQd<(7HqOI$#=lXnp)VB=H2u ztX;_OQTu`iliZk~eF8ivn&ZB%M=oLXYLb{C_Oy0qU#+zr#wG(UKFem?-JoezXS9(> ztupw0R%xBcJT@27HMFd-y!ei26LPboWZb!c4RXu-xrI}c>PY@7%%ZCF2+);|Ug2Jp z{NZtFX`hbbSb7^n=n#v2nf?5Df&s&=ObX>a4Op+|2smR~#hKm9Cx45t8(I^;k)@`x z(T?uL*f>G!S3Vqeg|JWfOuH0fd*5q`K8IiANLalBBCxe_amnjO-Ka@Q1_stp!&P1* zvlIKziiQGtd;+6)*uDUY`R|&&xlarM* zBb6#4(l?p2w$qF50KcD6lF1H6U#$tt`glnwag|bApHk6h)z4fw^H^}o9dgOPu3G#v zD^R0gCV$C0DV?f;$96=V%}^FX0bY!}EK$RS%e_hvc17O?uUG*HtZ^CVDx7g$xb=zb z;3ioPS4k*n-+3>fJIM8wmG%)k10AZ#5R?;#*wt9rNK*@>0+v zgZKXR_{Qwx_V>@*1R()FyyRGZ+|SL$JO3kecRBa6*PmdP^UYyMRsM<~=MD|RNwmNEL8ya8kS&-#S4&NsXBDJ>GIzOjiR*6zO`Hzng6_1*&=c&gyR5T*!VDwd`h_vxo z(vmotDN(0Y2^}T9!P(ZzH2u_L!9jFotr;<4wYZp+Lr-G)lwrgMfHCXSuB%ELv-=I;A&q$S1bMPnikJ+X@u21q|BckIm4CfE zpXpCFtP&%CaeojqG<9)%Dqa)Xux-1amCZ`axrbYUhYf%wo1h!edeu(b1xZjzD_ZVx zN$c*Mv3M?%v1-7uT>?{!1~s3OujmsAC5C;&WfIhR5Rab_!q;DW;H3a{h0*D~*BQUd z&JT^e0ablhC?}ifC)FqV$M8fnSyLg$Cx)9*V0xdTr!bFOK%V`x6)2M1sHvP9x!8v@ zr0?`}!GzF6{SKm7rB*d7OjyQ&o@iZk7$(phSC(_SsI6L54R{Ok6Q*}Y@eRYIdjn4)C{R*PPILTuV6lwFHsevOm|Ha^P{d!x9N_~EHvr#yC`GIoy@Gv@Ooz*zBJ3INP z;ic&2ID2XX&3Oq}*6{xJ07}LxxOpne%J0M9u9h7gD?bW4F(pR%S5elBuUEBns7prS zX{7A&u=Bg4Y`yf2%b?l^W-pr`r(4QEL>-TuiMF@2kKVyDu_gfBNq!j0#Qbmmy*&W2I;uU5f6FiCRYf{|9z<*-lq_{A2p zd-&o~X43<;E`-qemf(@XyBWgJ5`TyqPXA71<>Xb;DW4j>3HrZTUuwxrPvnor{uU># zt_H*!qNC%Q20dzB1lxx^V(U^i*7|;qF-d%$-E>kxi{L$E*$mY>QpYy6(0rVp1FwUsF?A2p~>zB4iPKi{+ zr{%@2T|t(U#Ec0nI`Pku1bwbictvFBu5I}e$)5qPTr<~UW3!e zcXp8ueY1B_E(0u*kO{Ub0eU>E{0ruTk5C>&F42Rk2P5M`Tk<0{4Ry!!WQ8WgG~*0H z@MNUtJ*Vg%MjDNmGBAO}0HJ28qBv$AScATvQ1`_ZHz<^CDQL`R!AliV9%bfbL@Lo-t}J?XQZUs%8{5(XSy}96p&(+Z4LmSM6b2{D$(rN(l=u&7_eQIC zmCc@J>N=ZFoHaYJt5$CmPQ;{^)UeOonV@P2?COHzKd%SZM1s*abRy?KCrLG2vJTji zaSM3UEWX}Y1AAPyHsUB5A&HISfb5V5^R$7$W?1ApGl7D!OA&4gr|p1R&3UTYH|Sf8 zh+-qvLw5z#0>-vM9*?RafiwJ898W}#7pf`WuFQJVU-(LFTRs>%U1avH(GEXB<#S}Bo!hgRfxA_&2A`I{!A> z8iXb}!#af-z>u1BYJ+O^SNq~(K(XrsmV|E2?U1bx<&v*=?^LLFmyKEf>wsqW6*nj8 zUTO5n6^Cxum*o@@1 z7OOyxUvcQ{`YD%rQn*0_eC4pm2+7qDSDpMvvZ+r7G5~^IadmcxR4=r^u`Uco$-AgV z3w^d<=s0l5&5%Dz7T{zrfsi;qYCBe;iZ9iV&)@HLd)TtUG_f$}+i&viivS_vJT-9n zXs)ZsT;kzXzy@<(Z`Fd+N8a& zZ%?*(^SQH-Q)VN=L9Rzi%C_>pp$!30HU1O+FCEjjFNI7i;~EH26it~YFA#Kc2W_?* zba`&dsmAH|_lXG-hV&Kq^Sf1sC$E7O6$-X^#YY-xh1u`ta>l}=Rq?!SHS3i6i5i{N zn~P_+kM<+UCUSYzw45s&b`wSziBz3Y+0MzAw16lfI-Ax$$HM|>m3`X_KPXjKp<&p* zahNX<+jBx;=qsqNla~`#BeZ)?XXbC6pRNQ3+wy5S97X3G20#eLD&*n#42v@Hx~e4RT2 zTf8v$O0EVgkCx27L95Cvq|CcdI`NhHmb1LWrc!vbuiD}bxLOSben|n2JP0Eu#(4pP zJ2+*NZU@^~sHlQTFh+fVd+_-A4_Qoy3OVZxXj(qgH-C$AsbdN*(39@98=J5$nbve_Moo5D0>`x{g57UudbW zmjEnZBKk?p{1WQu@Xvf z+kcW)K)-~PJn0j^Hn2@LY3Uf6zaSXwR39eku2^&0ozDre0xwX;-Z=S~N#Ui8yMBgsqJ&T_KZHRK{;Z(ESzQvsol`YNBwS&}N}bnL;`XN`D){_?wSI zeC?uN#9Du{mSfHqJ6+p9bx9Xnq=IR4;uOg3nyFtjrdDTbUzcm}>RpzoU$z@H<`8_; zQIBE^wo^CaV=PIoN1LVn8H(V}&)Nn%*^aIShb&GMpe2+q!F*isJ>Xfx*iN4j=CHBN zzEw@MHr688q42MNX$-N~=B&s$aJF}(=*3qK-gV;dv&*vPFcD=acYROb9Id@8?}_kGY^~mOtau|Q^i{T@L1yCad^}<591{XTpwwiE5qal!5Y76rc~Bk6pT+Z{313)^V)DU zxx2A0NX|rnhx;r-#i5AbbrC7t-L$r-x!!e})bAG($fACs+MEli2 zXPxbpoaiVH=jioo{>bGN5?H4x5J9bOy00ln^Jn=LG@-DBs`n#?n}jk*67sp2qA=5? zS}xkf_Nd@@H>~$pM`$fjSVo|xkS8ihBpP68=9g|N14|PbA$ba}Yx0O@PFiHl6|;aYN9k1nP7JFul6I< zOPfaxFAg{ip1ln$ELn*{f0&kVn}u;<;~pHt?mQ-^XBv+wP2`Z-6!d(p>&i*e%$ydU zu5#1R;F%b64d5jW;t|oSZa>}u;&1Ers6l4b9ppR1i89K5#K%YojEhwDKt=y6Q0)ay zPFJ!O7oP$Vz}Sy3FIz57F4|)ti=lcIlJ;_VB0nCU!VHz`enGM5gW+oGz!~|?bxw!c zR+{pWm-R=>CPF0AI`J{ZQYDsAvIzocs8ozg>C^^l%0(3JJRQCK5dy!brIP3o&OZQC z>T-fp4wKPY5-8LAl*$9qC6k(6NEzG?>hKTkXtq1uiTViVlDI_!nbu#SdMIZ{^SZFx zRfx%V#ccHrT{1Q=;3O{y#LRPt#GOWxe?{KzWK(jeF>+-6k#98&q&Iv-MEuN<++?X} zm7y_a>Sb*a)b>kpy`?A3;v9`M`lfi+9FBQ*7bVzG*yaV}dlbZgC9Y9C zG{7yJWilE2gcU|NJj3P7N;;bmnOf!UU}2tBJiejwBT|x~qunEK81Q_(IuRKtqWZpA z1rZEJqaD~>5S!Vw5-$%rz9Ygwcj&CzqjZ3(P2%A+bbKGaZDqp7+W9xswWgub2*;H+ zN2pxfT=l}cM_m)Gp7N83jE#g&F^)t3Elr*;#iTffq8K*MT!qAhdwkgFJ*WE0vUnB2 z$C-z8HL&I2=X|m9lKcREm7msZw*)C$+O{r)DHJw$kjk^MAUGC0(F`s3OF_^FwFCae_`fT$5E&6n2!K1B8$7&qmn za3@%-a-m-pY%_<@g&GZ|OpD`m_@NDK&6XJ}c=3D}a6_o*C znqUqFZXs>@h?Zlz_OON&QijtJ9=3rZEH*Lv39T&%ep_b_O=I++Ghjc6FrDTA(o&^~ zeCdQWVEK{~Ht?X&H}mc?YESX4!pl{7{q!bf5P&{-7O7gA(a{b7Q5KY}Nfd#6B2HTo zB2pedCaSdJd&^}ON@}2za>O3xtS}$);$ITiD7ZX#IMSjaC+RyxKh}DE8zctXs-*+68PoYg#PhNWHU3z890?`%tdT>ha6FTR4*2GW3tQL8tS z1<4qYl$m&L+2jd!OXz5oI)#`zww$<+gL>Yt{J^=3mnR>ynP>k4xZ48JvyxjNI-*>> zU!|t94{}fy(w$BTb}Sa3RN~}c)|GKxf`bg8QW+hB+71C^V#J8ck6~Q093sk2v9=b# zw1Jpltl+(5UjRJ&G0W~ zY;hc$ViBaMzSR`-dB$*;v9a!f9xMpZT~_0_@5PwJ=K6AvACAPNZyR)EUXp1%=+9hH zOi*nmdM%HW9_WoJQ-<~PHN)gM2htjE-o~+Qh-PNu+uvh$T{gvd5-E8r8 zRMX#SnukSaoAECKLqDw_9=erlc9o07c9y>bX2V~HbJ!jfuU^`98ZE zd%+9Z?>c%8P!kHN6;k7TWfpg~T$$bO&rYa0b58%hD5}4OM+lA``i1i$KpWZ9x3CWH zOiBQ_-A@T{%ITI>FbF;{0fIt!)=kW=Se~&U1b^p*6n=jfHS=c_C9UGt60DN9j)9BW z4rtm9q&%_BKx+>J$yYP)i^;4&N>`|>hs%Rv921T`NR`0KN&m(D9m7#q9-1BYlX#L6 zN*!<&cwH%W^JgZsi@ur+SpmE$dBu&>ZE%3q&2<*I7#v_Yf-W&CD{Nruni@#fGJ?@K z=NCtJ7J{%=qvIaeT0;dw?EsM)(v-uX;r`U?Ur97^bwiqc!fB1D-mt_}*(n;I|2UrhJKnC3QqOe4^;y5U?NZuyg0 z=FSycfh)BR?Sf~TX-dGP!lW2onN7cQCL)bx_ypAb6jXm^_Kt0_VDV-O5&a$M%YV5< z$NTd+esXog;X6)8@{y-P!}Q&qUrk-JHiut^H9C_t!ZyoYyeId_jYA5j@Q~u*Bp;=7 zT1WNNh;Os9)Is1bcRmY**#OT$Ww;kSUiOC7lzU{O2YJDA3voK5R^U^un9e&&^0l(+ zv0I9e9%ib&$6%ul$9-csy=uhGb+OT?=Cy-m*3x{@S6W-{#A@xJ*as39#L0{yHQ5Fc zu-5T&h&8);?EXC3S+l0!3U4zihCPSwNFfF8z*JU)m#- z-X3aqmqCI1waCb|sH|;tf(G>?cghhp!VaOP9ha|h2;K?yP~Dm*WK0&aPfIySjWN%{ z7G^@$Hf@p#4u1Ohuzc}j)mbj14CDW(z}A;p1-x6!{K^?VVV123v`AzKh$l<|UeZbl z)NC#|>tLPcJZhTVsrHLKJ7gprj7Ndja%pGMaAH{G6xHEBh=dv#_qwWcr8oB80?s(ll7lW{_x{UI5r@2TT~+z=r;a2KY%vDUu@SyTh4O(}m^N7@-Y}yz z%p9s&j2f&S0KXU;2F#5iwQM7tB>Z@Vd91aIQgHbmx5Hz2*MAM~X#r`{SH%w zc$n8u+EH~%xvro~!T{R*qRYZgbH2HOjfS8$D&@J2p|znps9Cp7%WwHSjqzW|XBXmU z0gojDMoIVb#1;~UanRC><&h8mQFpVeHcXfUehq9Q@vioc`=$?Z%OLr6Trvh<_joRO zDzgii)moZ>kz5&oMJW1MDephcU9}ZDR8_-KK4VUiIU~77YDKXuV>gNe2?-%tns%EC z4-X&BoSqBNlKdWOG^)qMmt7P8!*&~W-qx0HzAnDi8E2~xKrNXR+MO%)Y7BcU2WMMZ;xk@(clwR)qI}ev{EsXFM|#Kg0@#V*r=0AbP^73 zRWDSZ)JeihG%2K1dvAtZ>vQIlu*ceu<&=?Cu#S~j;s#L7?5A}Z+!$t{_FT?>k#L`T z4GU)*%6!ZB5NSOk-!k9xXIBzpn}u;$hE40I26CNaIcv0bA79Kf$7nnBp>b+7pE1nqUJikF`q!X^Gfi@?@{Um}zXyCzfD> z`M1a!!Q5X$1diJ%nEr6*dvWar`xq6WZnJvxV#zhNmHSG5Aa4w9XQmH~aoP9O$I--hS<@?x!7`al0s^XaO^PW-t06F)VhQF2 zmqQU$@8<~slkfm|n>l_)9GsoF;rVpJNNYGR1IK$?OrQ|MO@7J`A&EoJ1n!~5hDS0qP z;!rq_S$j<%uI*m#G7;Q2WB?lDC&dnwgNl*nrtg!)+4MC`k4Y#E8 zpeA}NT5(yrq`lfKbxS~3o*1h;2U0C&H1%daN$RKjvC01eW13I(0_Lfd54F&+mKO%6 zJ!Q@r8kStuRi#;Uzf7n8S+W*L7V2#Lb750SRS(+krC8Dlk!g{*7gkwlpZPVM!XIl2 zUG`*}fwI&CN`04#pI>jN$2e-Ws?SQwxx&|Xsq{9GUl+N%to|xcNWkm)v$}4S)1+K{W`%j}}vf=Ud z%95wwA}7%!U4Uxq}f2MiaPVRGRXuSsI*zi%Wyv*m(i{I;E zVd0_Nu58KA*Y#Bx9q!M4FP{%qg|Gfab+G>6ixj13C*VM zKeN9-kNlnOsOul69BY9p$WJb7k$X2Uisp452EjgsO)9Of&Ah02+tuj%f|Vkz&4JYo zn^(*qxbRO_7{lcO=B1R}tDMn}O2uvG{fkf4DZoM#U6B+a0oF4+AbKOSf#8D+(6<;% zc!;8*Ss`+o5^#t#78-NwUaA;x1QVv3q3juk4a@6qW*7pZAwpTXVP5 zb-r)HpZ6!-mb&-PadG8ZFRUg~zfPg}-6q{+COQm$5tZ!&30&m**OrYPHRrb$7V z$NHjYmf%bi{n3u}g=>0zX$o~bd6*{HC-0MO+$tWKaKM15QL!XRg#L~J*@hZU~;+UTE^YV>(`eQ(l0Os2bNJsQW zq=>Fe-~uw_=EN!6tKHazz=zVd-z95FE!=mZ8j>fCfX6fD6w(!7j!fdmb(Bk+|IWQS z7NWyS0!jFngJldkhCDl%Sb*fOb`#<#@6>1o-fo-^8LAL!R%BQv&9H4E{GtO2h17dP9S;;sI;dTfq8+%*>{REtYG*Z(tfyp3O2`_Gq9hN6z($ZZJKxJw!Kx?J09NGh z7L~cFrYutELRC^PxyG$*%RclK1e$L%RdrD*t6daF{WF;XY{P}dtxNP%W;3bzQh}3K zxh{>iXvON6oSa&vK5Q~hnB3pgepPb?gKNjOnNtB}4`U3ZBz#I@xI*sp3q%|?xAM1k zT6BSV>rYA>=o7`lmUvAR`KG)3Cf2Fs4qJB_eKlribzY_)|5^E9CC$@icpR$1U^q4M z>H52TdMLb?2p%~=wi$|(_!lKcu0=)aL--i^qo~|#<)m~Ik1ZKkUe1j5Uf0UjpVPlv zv=IsDeI2b=^ql{+k|Gtj4nQ7CF?+q|3(8cjh}k2ArA?WdXWmM{JT&-Sr zz|jpMWATkR((bdCpK7wUy^VytN;qU)DtmtgKWG8=P$Ojkn@YD|-`3$Rn-H@O%+f@2 zrXivXe>Y-OoFGZ56N0>PM&+v+om!{)tnaI-1-Z9F?X?hc0J|@iE%fDvbS}Snfx~0cMw8BR@WxccOZ>5dPmye16 zuZ=1j-tFZwO<$+o=JTG0T;(z>ZgV74r?Lq;tLKIPGf%rxAI*D-|Cy=Mb}|bB_p3>b zDK+4;reTO@#2U|vm{HsS^=H8!`>1!AH}A%d9rwhFO80sY2RcI$Z!4`26ar7!ie->y z9)H#avjc);Hf@?RtgweL9aK*3>Q2j1(~FDL%VX`$fl-WDj2%|Kn4!;x0}>!)%P9^} z@nXCn`yZNv0C%Jw0zP6W##NlSWs@NO6#;N_ z9uBcC3}Q_P#Cl8){(r+u0-zPkFyR9s!usCd6`Q|P+baKOsq05fnlfC(Q07&fc-7{A z-#dS%$YWjU3*9&RqYtdXnIda(rpSAJnI!252U5MfmiS;b$Cf$Gh-1|?Gs?(Rc7)$% znFJrWHRQ%aV7`aLbAEMYzg_nS3Bg2K7tZ75XH@1D-C3YUF3s($Qio=Nb59|K7qchN z1ugNZOR7@+xcoIX~ynuBFwT8A^i5);;tmX%pwy z=T8?IR)S=e>xYwc8c%T$4?I>$GS1gS!OQTn*rG!+;JN9GoPJE~uVk`YQ)io~2%x9s zmzX!(pr%S_`G2<8*np}Eh^NVm^eu#+5}FIT%n3ZTQpYq9(u3#f&*2e0R8&EkMjAhu zxRSO0OF@MeKaW;Y0!bIVHtP+3n0_ct*uW+vTk;Mp;CHYrql`KSfot~o9nB#9$@9G) z8jDL?P{D1lC#9NNuc%S0L{i^(=?YA*TAx$k__QsB%|HlHe8sHZ2}NxKxXhm$nBD{j2psB%JePTVzw z?3;JSDLM%|)HXX-whHPNr5=B}W`iHL=YcPBpy`e7zs1lgsNJa67UluMVe;b#s-K2&b&l zI3e{z#{fD-D!0MiRSV@qjn@zy+O4sIhrH4zxaVfs-=PIROUOkf4ofx2ZgX-NA*Z zAY4jr5C=$ONgab0t`O4;L0;gARim81_^R&=%isYD>qzX!`+17EaDPm8?a5yN_#wl% z*SR*=kj98_30;DcA3KHM0mK{pAQHd8LV|&Uw-^M{`2n~1 zjC+y4-7wmI4UGtc_`|4CPVidWB5!SeQvWKGv8YbC)FC~Llq$10OC zVo4)Dk<}a^ak{aqtl%Jph?Za%sIxz(BBcw^qe;*j5i~>kGO95QIQ}B{%XIGF4STMG zb5hxym%Rd=vKw*?Lcm4@W}#c71sUbIN^HQ+)}v^@DPR2sawFxSGCi<@E;$f_Sg1Av z5Sb@8T2q;@$6No^vBfQp{_V_-M|T?1omDiwO}6ptNmI~HAVGzJ79)-doL7t(`#Wk_ z#OF%EN}iJhikcCHQQ&ZIZXvrA)s8jZC&PzEU1h~*aswu~4pWfM4gxdy>%0O1e(70D zBXiIE8pCP?Y(~_I5}0L2ZWhm}_WL`r9;Rq`t=2V3TMr7%?E^y-4f@`THUiZ7HHK3k zAkcI89Xhq0j~#M|$)XV1b#xV>kMgV8Zh)GE$Y9L~>EWYPmkOQ3&5c&%nxP-ar`~o=|0DJLmhfSDiVZ zG?l`vLtkvFf0@=k?=(W#KP|3|O8F;}^eq92cCE_}ayDASG*CecWWm5FRzSNMqYVqX z96as4fSc^-p!3EuAcmB1GPcY~#UP+MVfX%zA&&IA?4*PmzN(k9!LK9xq!f=LOs#iD zzDQevpo&rSWE$^m!>^~1%@OvLfx;&Iv3(JIz5=rt6j11+N(TK5 z=v{P*=zcOU)ohpU6r@b%Km_~3${z^Sg{_!`ho}4lBLZlDCz}*{O?T50Wi4)ciLOYK zdC>fQh^LUrR$AGRJ@Ls`Qb5!^NfrW4F11aFWOK`ciD;-8SSSW<|D2futI8BmQa15; zAZYa_I7b?2Eo*H+0-JLMUG>G(%Nxyf<6#VUs88!^+z`llo1H||ymz;wj}R)9cfv__ z(-0`Kp!cD>e;6D|_&9!J&<@>IsOHGs@=^AhgPis3qxWOdoE#vs*b-g3 z9}#?|AgU*dUl*h)QA`A+TEauHh`(6^q`-D)<%yc;r61UJq(N&R-XKxYx$6%@jjsKy ztWy>xU-O!zHnn=BoaCkp@dho%_ga}PcB~&XPsf8zc46Z{fRTHp&TC|B4lt@#LfFU7 z!5t#u#%lnnQ_C)$Ga4M}Ggem3#Lfht(%NA#(a8#oqQC1ubgpA(;I5UFXY6ueW*%Xy zkQVuhU-GRwFIx+w46I$v*TAgy{oqcOupj6 zh4`I<2*L-qt@rI@5oE1@(9LoI20y~kc5s#3z@MjG>!`H$wjTvLAVXnyaJNmV%Jz86 zSUd5p>FMq1Q)Jj9V?g*IGF;GoW#}no^tdy4#hEt^=eXJ5r^NcQJ=}-V!-g|F0Fu(d zhf>e}Qa7TOOrgCJagD~G!I~^UbCDvjl)zI3OV|aWH{vKxp?NJO7JWhag8*d)8}&Z| z(VvCu|iBX@ojE!abKnMHTAkQb7OeAl3Bjh%9HG#9rKhO zfU9iH!&STrpzeOo3!w7-XCUjdL*>qwR!Ep>J*ZNCpe48sVn1`^*R>MoCu5M_Vpq;pB!4KE6F;)Jmy6#E|QL>_%~kjO&)nDB6=6FRAU_ttAS zSU;i}dV@Cre!}P(VL2I319RZ*7(ZWiUx)MSqXkK)Fz zXCD7{BQAR^Dt1%EB6?!!vSXlFlf-%y@ky2|L`VFt}?)!g&I5XQBj5OwwijOsnIKbf5TB zx7BvW3J;{f^GZu*|Nn9JDhL4kG`Fp?+5{5qxEU!49*!f4uB#$tc_T5^py+C{fn=8x^=P?p!Tli)hUg0~kxT++_Fx2khQZSKzy);#ma;?pM6p@2g`KZz7YY zUcLs5E!K2@?6HKO8EFBTRjfVf^sf-yvMT9x-m|LV6C`<92afQ31@Yb4Uljg@$SVA zONRdI)tW;^*5}?!&30K}RP=3O%k^CKM{)blRowZ?I$XK!8r+@B_7#HO>dtQv_S!Lx zhjVc8-;neEBDR*Rr~^lF`*&WLa;qM)6L(Pu$EKYQ4c~unPf1$iSKJXeZS5X@Io_K{ zA|xEiX^#9Xqax=lATwHwMk3aF0MHkels?t~_e>`ZIAPd;;uu4KBGDfb6xE8LUF`f4 zCpPfr)0_ZGY7s|HFFAB;>9yZnD==BnslFs_!gX4m=|*Ps2g>3~+T&|OdANLMnP{ss z{rvYz7*PsVeUyHh9BbN2g|l<)Vrirpn~SEP>a0sXrjCkeydXl2>Y#8~`%VM%g!izJ zvX{ZCGvu1aRfE9nRZf=8{IPA?qS=DNsAPqG-vcj}0e--AJum68=$LSe_>n(bu(f-9?K zZJ>&*H|XgxN0EkAQ45r=xa^0a>iBjO#%d4@N39q@9V+}HQEVIIv1JPX+CZIjT;%Ez zRiDsUhRX@SPoJz4L1Dl6`-FM(E%GI2@NsnnC+9)+3Mh+UdcxQ4EM^#uI?O0L%zScC zL#Xsf+cQE~sY(?!`#7$(k_WL^HljH0keIL}p?y?Bd(Z z_p(p}a&E#z*aF56AfU7f%x&@~&~p<$x2f1C!&`qo2V_$$j9H_!K)7{Zj`3$$c)M?~ zuFe&>*2eo@Je-KGJldY;uieq5s%{MH)a#!}Jy%|uM&DiK>zjJ{4da(y*jJ8DvpkGE z;|2MFKkw{$Nzr(=;CIZS6rOp*j(YiQnpsv-_V=edb;1(2cgzUPJG$M5*HhPGRQFNJ zK-ivUn-U>DhgAk3bmrKPNm6niC03$VQ)gjxQQkslQr2)x!3|yPQ&NnO z_%7Dix$}-0FX~JuyD03<<-Wev(uenM;DPTtrH5Oq_Uwx`9+F!!zaLw=#RO!2{wd&$ z%A4N%mort&q9o~xXc1*axT<_)qO0xtK4_+D_fA$>?bAV5C9F5JYofQiKe6~9hp(?m zK(B@-U6~Cp7v?3EwvNRQgX95e+6Zd{GQn@qhXbr}x} za{W7I0!db}Q}-BEx0FG)Zt0-U8Rhr-&okTyRw5maaaHK(9rrN7pBIbCNtl|g`8fZl^-u9m#HK9;_XB*_mVG*Px>a0tXX6<Fz?@?%69%9iZOdHj0l#tviko^eJ&$Hp6U zH8f^ZYmOshhW^E`T5it5yzh*)zr-e>q|VeV&qDvVkU5_nK3<8tkmn$car!mD6$Bo| zg1f#W$c0B<)u<ed{vrW!Vywr&B2s ziS;2ghUOXuuAd1~YcHtVD3UyPkO}Y6&kiqCd)3U<-`@-v&9v!D zdjt(z1RIGC3!MiGmjerz1q++8&8p9;f42!b5jwW=g?p7cS2!$+^&XzoBWG_+LN1GF ztsS5dFPm43C4Yf>EWq&)z(~3g$GYpaHIGiZ(6@^@KEd#wj^+{Z@PoCb<~{;V9o~4hbAnJN5CwD| zK0qiSl!21H4rU^49vp%=s;}nt`Hz zNCq4Ebw6p!4VRrPCPs9Bs?bVd7PO&<_|aRsFvLj>79`LM3JVsb@S<|jEF*bzu|?7d zosBGC@!FZY#HSC4=f!s;b=JZvX$Ie%UB9<@dLc%tmQx7WXoG(_^<1eY1~h3T!&SmiE~ zXj~mBcNZ@k?f7U>M;X)jr>f5=*|j86k|NEM9&ymkB!}7Y`>LNlA$>-uGU-uHH(58G0PmQ z+*3#eH(=9d{0JinIU_2D7Ub)ZJhrC3whF%?HfBvAVrRopiFHiO)6b=z{q@1r7e1*~ z5?g^RwuY%3&PY}U)b#+w1U1Ch3 zmLM#`2}g4L!JP~c`Mzz14!N`i!#tUpCoxVysRA1cT#xRV2qg>oU2cuisJSIxG|6#- zJ+;axhoXO7#pR|bUo~$I!V_S41UJ7E>G5>=uF`o=ad1bmd=$9;3++|GWVC@;ayu`) zT6_*Bnd>Hy7N$b=x$Xsoi#EgQX*jZ{)M4<>kc9RCY=|Lq*8mjMQ%(JvfYpy3O_!wTw5C)V28t7buK&} zPE(sFuAm}l7J#f6sPAC3thCTEE!P1$A>+VzsQGSm`He3PMTr~#Hq%Dk(?=Sxj5wt# zAjF=*{%iuO++Is?09J|P?V**sF;G)drD!7x+xgs7w~Jy5xzf_f*|+|tHl(y#<8Y&X zKiuoIhFM@4QW@e{8i=5{nhqo6j>A)_-}@+*&EB_~$@}jAgLNli{xebVpn>N{ukHgPVQ6xKBzWkuKcuDJ04QMI_UCVKQwFiV(65NdwJ-*p*5>L}t zmP9**2NLDU^_oEgg;jrg)fatn_PD4`9!;lFum4}R8+XO~ZjtRjsTXxVoru~H7pk4M zN|%|O8gpAW^6&42f7A6-N;hYZqq(Y8MXjYuPDiyrVP{<~QiK!Q@z>1mQ zZ<~O$edt^&M5Divv?G3S4r6EezY{xfwu^+jyR$a^H+AjTQq#-UAS;(o?~m{zesQ#5 zBgEcUwU4F&ZZXK?V-*1cZ9=KqgBtgA-<0#|on#{oZ=n79$cyoAil9k*L44pbda(q1L+lS6}2ZIC&SPZang~!tU&oV58-z{;Z z)Jbj`r5BO&V8kg~pYx{0p^9rY&UN8$TYvCLOHfc^3lNgcCo^&xNXqikA?10NO{^v- zL2m>}6Ul)u-jvys@%2=_ru4scpb1Fr>`~u=9*A-0M;9%4Dr{b@0XU%RC4p`tO+||# zZ?QCiW60iO^}NiYd1@YP>AAqTFmx82Mn(jBoMDmeo+$||96Z=TH=v{~cUWJq`=#YZ z0t{q+SZtQ~OGqhc#^ z%Rqlnsr?k@mLpT{rt*AHpO=O6v|jTY%1&cA4iad<^;RYcqbkx7>Ki%g75q`y!s=Wc zGEEdoKH>_YiVtl1!ypiwwwCOGeK$f_Lno3IwxmI7tA!OB%E*hb>cnr)ZJ!GRbEYl? z#+f<{$C)NH^1$CGgrn6n5N;dvNmtI=;9`9?!r?zH%z;36V`lw0TfQ3`we|5YoYzM) zt6g_4o-utwMnxGe+LBTH# ziJ>6o4(7^Tj`oZFlcdUNWz5#kh5dG1&`A?#=BP7jB0MDJYb|ummNrd1(-_`K@4Q5t zB7@+UU^5lu(W5fksLktJY%G{ETt70YX8x%Fx&Ihvm?Ca^--sHe`vs%%`G04aoN-6g z8(MZvG!_(XBMzwb#iRd+{N~c}!YKZHe7qdbJSUw{WtPpYok*Nl8}w6V2HgCi>M)#D zE8Z9+Wm8 zu@gSL6%7=#x`90*YNwi001&0y!zD+@z>ux!E&Xws zd63ocAmoWaUhHI(c!Xj!4cV)@@}m5c;{EkC8HHXuwr9*wOIOo_BQ;s~*@&<(l-nJl zf?;Zax(b?Tez6(`rMdm5*di-ics)9IszKR&=aab8WY}ivTK2l(z$c5AG;jgAOa2oC zjQx%06OZn`gM&f4q=V|DNCdPFbPJUMV$HJhN>5TDp}31Ot(UiDB9VfL1fd);CW?nP zgA9|w4O2$oV{ru1J}jXL$BuD)0hCI7!ZdInwR*rpzWWuxgSk%E**{Jg#M=h^FSPEPAdhchpEyJPBh>Ly0-=15HGN*Lp!rn#JC^NVo0U5CRw z%J#oY>&iS z4Znj{DB#bJ-+i`qO*W9r12i36h5EXSwf{}Q8hbwgd=*PN983Zbs6~)$r8k$GGwTnftFp%cn{2V4P;^*x z*JK{}b@T*K2?M{i3&R_ohdmwXPw{Nc zXX~yplCyg!WAaDVSj*JWoKm|JE28+FzFGyELiN86IkY^~PLdO&to7L`JxDxi= zgo72`y)wp7$myxP!Y);-4=I5~Mf%oFb|mO0SNNO+cTs##N+N2|FbtX1gu-$%gB4MU z`s2>t=*|=0>e=V+H(XwmtL(@2@zetzg;b1T&Qm? z9}3)Jym3_*0A>@u8}2Qg*AKhN(Cv3zO;V@$jA>n2tj1y$;{1Gr{dN@el~^DVMUXTq zafY^KMX}WKR^m#063n6z1vlad&k)@wpqVC>s9^U=

    ^QG##WW8T`~r;AKIG(=H*v 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

    ugyE!+T5Z(9aF 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 nna2ysl(Oy?5tAp+>M-00960Cg@Bg02~(p(VTdo literal 0 HcmV?d00001 diff --git a/charts/hashicorp/consul/1.5.3/.helmignore b/charts/hashicorp/consul/1.5.3/.helmignore new file mode 100644 index 0000000000..3fa2f24edf --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/.helmignore @@ -0,0 +1,5 @@ +.git/ +.terraform/ +bin/ +test/ +crds/kustomization.yaml diff --git a/charts/hashicorp/consul/1.5.3/Chart.yaml b/charts/hashicorp/consul/1.5.3/Chart.yaml new file mode 100644 index 0000000000..f24d803110 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/Chart.yaml @@ -0,0 +1,37 @@ +annotations: + artifacthub.io/images: | + - name: consul + image: hashicorp/consul:1.19.2 + - name: consul-k8s-control-plane + image: hashicorp/consul-k8s-control-plane:1.5.3 + - name: consul-dataplane + image: hashicorp/consul-dataplane:1.5.3 + - name: envoy + image: envoyproxy/envoy:v1.25.11 + artifacthub.io/license: MPL-2.0 + artifacthub.io/links: | + - name: Documentation + url: https://www.consul.io/docs/k8s + - name: hashicorp/consul + url: https://github.com/hashicorp/consul + - name: hashicorp/consul-k8s + url: https://github.com/hashicorp/consul-k8s + artifacthub.io/prerelease: "false" + artifacthub.io/signKey: | + fingerprint: C874011F0AB405110D02105534365D9472D7468F + url: https://keybase.io/hashicorp/pgp_keys.asc + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Hashicorp Consul + catalog.cattle.io/kube-version: '>=1.22.0-0' + catalog.cattle.io/release-name: consul +apiVersion: v2 +appVersion: 1.19.2 +description: Official HashiCorp Consul Chart +home: https://www.consul.io +icon: file://assets/icons/consul.png +kubeVersion: '>=1.22.0-0' +name: consul +sources: +- https://github.com/hashicorp/consul +- https://github.com/hashicorp/consul-k8s +version: 1.5.3 diff --git a/charts/hashicorp/consul/1.5.3/README.md b/charts/hashicorp/consul/1.5.3/README.md new file mode 100644 index 0000000000..d917af676b --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/README.md @@ -0,0 +1,109 @@ +# Consul on Kubernetes Helm Chart + +--- + + **We're looking for feedback on how folks are using Consul on Kubernetes. Please fill out our brief [survey](https://hashicorp.sjc1.qualtrics.com/jfe/form/SV_4MANbw1BUku7YhL)!** + +## Overview + +This is the Official HashiCorp Helm chart for installing and configuring Consul on Kubernetes. This chart supports multiple use cases of Consul on Kubernetes, depending on the values provided. + +For full documentation on this Helm chart along with all the ways you can use Consul with Kubernetes, please see the Consul and Kubernetes documentation. + +> :warning: **Please note**: We take Consul's security and our users' trust very seriously. If +you believe you have found a security issue in Consul K8s, _please responsibly disclose_ +by contacting us at [security@hashicorp.com](mailto:security@hashicorp.com). + +## Features + + * [**Consul Service Mesh**](https://www.consul.io/docs/k8s/connect): + Run Consul Service Mesh on Kubernetes. This feature + injects Envoy sidecars and registers your Pods with Consul. + + * [**Catalog Sync**](https://www.consul.io/docs/k8s/service-sync): + Sync Consul services into first-class Kubernetes services and vice versa. + This enables Kubernetes to easily access external services and for + non-Kubernetes nodes to easily discover and access Kubernetes services. + +## Installation + +`consul-k8s` is distributed in multiple forms: + + * The recommended installation method is the official + [Consul Helm chart](https://github.com/hashicorp/consul-k8s/tree/main/charts/consul). This will + automatically configure the Consul and Kubernetes integration to run within + an existing Kubernetes cluster. + + * A [Docker image `hashicorp/consul-k8s-control-plane`](https://hub.docker.com/r/hashicorp/consul-k8s-control-plane) is available. This can be used to manually run `consul-k8s-control-plane` within a scheduled environment. + + * Consul K8s CLI, distributed as `consul-k8s`, can be used to install and uninstall Consul Kubernetes. See the [Consul K8s CLI Reference](https://www.consul.io/docs/k8s/k8s-cli) for more details on usage. + +### Prerequisites + +The following pre-requisites must be met before installing Consul on Kubernetes. + + * **Kubernetes 1.27.x - 1.30.x** - This represents the earliest versions of Kubernetes tested. + It is possible that this chart works with earlier versions, but it is + untested. + * Helm install + * **Helm 3.6+** for Helm based installs. + * Consul K8s CLI based install + * `kubectl` configured to authenticate to a Kubernetes cluster with a valid `kubeconfig` file. + * `brew`, `yum`, or `apt` package manager on your local machine + +### CLI + +The Consul K8s CLI is the easiest way to get up and running with Consul on Kubernetes. See [Install Consul on K8s CLI](https://developer.hashicorp.com/consul/docs/k8s/installation/install-cli#install-the-cli) for more details on installation, and refer to +[Consul on Kubernetes CLI Reference](https://developer.hashicorp.com/consul/docs/k8s/k8s-cli) for more details on subcommands and a list of all available flags +for each subcommand. + + + 1. Install the HashiCorp tap, which is a repository of all Homebrew packages for HashiCorp: + + ``` bash + brew tap hashicorp/tap + ``` + +2. Install the Consul K8s CLI with hashicorp/tap/consul formula. + + ``` bash + brew install hashicorp/tap/consul-k8s + ``` + +3. Issue the install subcommand to install Consul on Kubernetes: + + ``` bash + consul-k8s install + ``` + +### Helm + +The Helm chart is ideal for those who prefer to use Helm for automation for either the installation or upgrade of Consul on Kubernetes. The chart supports multiple use cases of Consul on Kubernetes, depending on the values provided. Detailed installation instructions for Consul on Kubernetes are found [here](https://www.consul.io/docs/k8s/installation/overview). + +1. Add the HashiCorp Helm repository: + + ``` bash + helm repo add hashicorp https://helm.releases.hashicorp.com + ``` + +2. Ensure you have access to the Consul Helm chart and you see the latest chart version listed. If you have previously added the + HashiCorp Helm repository, run `helm repo update`. + + ``` bash + helm search repo hashicorp/consul + ``` + +3. Now you're ready to install Consul! To install Consul with the default configuration using Helm 3.2 run the following command below. + This will create a `consul` Kubernetes namespace if not already present, and install Consul on the dedicated namespace. + + ``` bash + helm install consul hashicorp/consul --set global.name=consul --create-namespace -n consul + +Please see the many options supported in the `values.yaml` +file. These are also fully documented directly on the +[Consul website](https://www.consul.io/docs/platform/k8s/helm.html). + +## Tutorials + +You can find examples and complete tutorials on how to deploy Consul on +Kubernetes using Helm on the [HashiCorp Learn website](https://learn.hashicorp.com/collections/consul/kubernetes). diff --git a/charts/hashicorp/consul/1.5.3/addons/gen.sh b/charts/hashicorp/consul/1.5.3/addons/gen.sh new file mode 100644 index 0000000000..1d03390bed --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/addons/gen.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + + +WD=$(dirname "$0") +WD=$(cd "$WD"; pwd) + +set -eux + +TEMPLATES="${WD}/../templates" +DASHBOARDS="${WD}/dashboards" +TMP=$(mktemp -d) + +# create Prometheus template +helm template prometheus prometheus \ + --repo https://prometheus-community.github.io/helm-charts \ + --namespace "replace-me-namespace" \ + --version 13.2.1 \ + -f "${WD}/values/prometheus.yaml" \ + > "${TEMPLATES}/prometheus.yaml" + +# Find and replace `replace-me-namespace` with `{{ .Release.Namespace }}` in Prometheus template. +sed -i'.orig' 's/replace-me-namespace/{{ .Release.Namespace }}/g' "${TEMPLATES}/prometheus.yaml" +# Add a comment to the top of the template file mentioning that the file is auto-generated. +sed -i'.orig' '1i\ +# This file is auto-generated, see addons/gen.sh +' "${TEMPLATES}/prometheus.yaml" +# Add `{{- if .Values.prometheus.enabled }} to the top of the Prometheus template to ensure it is only templated when enabled. +sed -i'.orig' '1i\ +{{- if .Values.prometheus.enabled }} +' "${TEMPLATES}/prometheus.yaml" +# Add `{{- end }} to the bottom of the Prometheus template to ensure it is only templated when enabled (closes the `if` statement). +sed -i'.orig' -e '$a\ +{{- end }}' "${TEMPLATES}/prometheus.yaml" +# Remove the `prometheus.yaml.orig` file that is created as a side-effect of the `sed` command on OS X. +rm "${TEMPLATES}/prometheus.yaml.orig" \ No newline at end of file diff --git a/charts/hashicorp/consul/1.5.3/addons/values/prometheus.yaml b/charts/hashicorp/consul/1.5.3/addons/values/prometheus.yaml new file mode 100644 index 0000000000..1f90636f2e --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/addons/values/prometheus.yaml @@ -0,0 +1,21 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +# Disable non-essential components +alertmanager: + enabled: false +pushgateway: + enabled: false +kubeStateMetrics: + enabled: false +nodeExporter: + enabled: false +server: + podAnnotations: + "consul.hashicorp.com/connect-inject": "false" + persistentVolume: + enabled: false + readinessProbeInitialDelay: 0 + # Speed up scraping a bit from the default + global: + scrape_interval: 15s diff --git a/charts/hashicorp/consul/1.5.3/assets/icon.png b/charts/hashicorp/consul/1.5.3/assets/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0798b2445de59405c89614338a3276bfb36d832f GIT binary patch literal 7981 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 OK}lX+u13Zp{Qm*> alphanumeric characters + => '-', '_' or '.' + => must start and end with an alphanumeric character + (e.g. 'MyValue', or 'my_value', or '12345', regex used for validation is + '(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?') + +Usage: {{ template "consul.versionInfo" }} +*/}} +{{- define "consul.versionInfo" -}} +{{- $imageVersion := regexSplit ":" .Values.global.image -1 }} +{{- $versionInfo := printf "%s" (index $imageVersion 1 ) | trimSuffix "\"" }} +{{- $sanitizedVersion := "" }} +{{- $pattern := "^([A-Za-z0-9][-A-Za-z0-9_.]*[A-Za-z0-9])?$" }} +{{- if not (regexMatch $pattern $versionInfo) -}} + {{- $sanitizedVersion = regexReplaceAll "[^A-Za-z0-9-_.]|sha256" $versionInfo "" }} + {{- $sanitizedVersion = printf "%s" (trimSuffix "-" (trimPrefix "-" $sanitizedVersion)) -}} +{{- else }} + {{- $sanitizedVersion = $versionInfo }} +{{- end -}} +{{- printf "%s" $sanitizedVersion | trunc 63 | quote }} +{{- end -}} + +{{/* +Sets the imagePullPolicy for all Consul images (consul, consul-dataplane, consul-k8s, consul-telemetry-collector) +Valid values are: + IfNotPresent + Always + Never + In the case of empty, see https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy for details + +Usage: {{ template "consul.imagePullPolicy" . }} TODO: melisa should we name this differently ? +*/}} +{{- define "consul.imagePullPolicy" -}} +{{ if or (eq .Values.global.imagePullPolicy "IfNotPresent") (eq .Values.global.imagePullPolicy "Always") (eq .Values.global.imagePullPolicy "Never")}}imagePullPolicy: {{ .Values.global.imagePullPolicy }} +{{ else if eq .Values.global.imagePullPolicy "" }} +{{ else }} +{{fail "imagePullPolicy can only be IfNotPresent, Always, Never, or empty" }} +{{ end }} +{{- end -}} \ No newline at end of file diff --git a/charts/hashicorp/consul/1.5.3/templates/auth-method-clusterrole.yaml b/charts/hashicorp/consul/1.5.3/templates/auth-method-clusterrole.yaml new file mode 100644 index 0000000000..6b8f2c5451 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/auth-method-clusterrole.yaml @@ -0,0 +1,18 @@ +{{- if .Values.global.acls.manageSystemACLs }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "consul.fullname" . }}-auth-method + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: auth-method +rules: +- apiGroups: [ "" ] + resources: + - serviceaccounts + verbs: + - get +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/auth-method-clusterrolebinding.yaml b/charts/hashicorp/consul/1.5.3/templates/auth-method-clusterrolebinding.yaml new file mode 100644 index 0000000000..9bd6c64113 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/auth-method-clusterrolebinding.yaml @@ -0,0 +1,39 @@ +{{- if .Values.global.acls.manageSystemACLs }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "consul.fullname" . }}-authdelegator + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: auth-method +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: "system:auth-delegator" +subjects: +- kind: ServiceAccount + name: {{ template "consul.fullname" . }}-auth-method + namespace: {{ .Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "consul.fullname" . }}-auth-method + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: auth-method +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "consul.fullname" . }}-auth-method +subjects: +- kind: ServiceAccount + name: {{ template "consul.fullname" . }}-auth-method + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/auth-method-secret.yaml b/charts/hashicorp/consul/1.5.3/templates/auth-method-secret.yaml new file mode 100644 index 0000000000..af0aeb4e1b --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/auth-method-secret.yaml @@ -0,0 +1,16 @@ +{{- if .Values.global.acls.manageSystemACLs }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "consul.fullname" . }}-auth-method + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: auth-method + annotations: + kubernetes.io/service-account.name: {{ template "consul.fullname" . }}-auth-method +type: kubernetes.io/service-account-token +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/auth-method-serviceaccount.yaml b/charts/hashicorp/consul/1.5.3/templates/auth-method-serviceaccount.yaml new file mode 100644 index 0000000000..098339b8c8 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/auth-method-serviceaccount.yaml @@ -0,0 +1,19 @@ +{{- if .Values.global.acls.manageSystemACLs }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-auth-method + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: auth-method +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} +- name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/client-config-configmap.yaml b/charts/hashicorp/consul/1.5.3/templates/client-config-configmap.yaml new file mode 100644 index 0000000000..cab2c7c043 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/client-config-configmap.yaml @@ -0,0 +1,40 @@ +{{- if (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled)) }} +# ConfigMap with extra configuration specified directly to the chart +# for client agents only. +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "consul.fullname" . }}-client-config + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: client +data: + client.json: |- + { + {{- if and .Values.global.secretsBackend.vault.enabled }} + "auto_reload_config": true + {{- end }} + } + log-level.json: |- + { + {{- if .Values.client.logLevel }} + "log_level": "{{ .Values.client.logLevel | upper }}" + {{- end }} + } + central-config.json: |- + { + "enable_central_service_config": true + } + {{- if .Values.connectInject.enabled }} + {{/* We set check_update_interval to 0s so that check output is immediately viewable + in the UI. */}} + config.json: |- + { + "check_update_interval": "0s" + } + {{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/client-daemonset.yaml b/charts/hashicorp/consul/1.5.3/templates/client-daemonset.yaml new file mode 100644 index 0000000000..9c607385cf --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/client-daemonset.yaml @@ -0,0 +1,611 @@ +{{- if .Values.global.imageK8s }}{{ fail "global.imageK8s is not a valid key, use global.imageK8S (note the capital 'S')" }}{{ end -}} +{{- if (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled)) }} +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (and .Values.global.adminPartitions.enabled $serverEnabled (ne .Values.global.adminPartitions.name "default"))}}{{ fail "global.adminPartitions.name has to be \"default\" in the server cluster" }}{{ end -}} +{{- if (and (not .Values.global.secretsBackend.vault.consulClientRole) .Values.global.secretsBackend.vault.enabled) }}{{ fail "global.secretsBackend.vault.consulClientRole must be provided if global.secretsBackend.vault.enabled=true." }}{{ end -}} +{{- if (and (and .Values.global.secretsBackend.vault.enabled .Values.global.tls.enabled) (not .Values.global.tls.caCert.secretName)) }}{{ fail "global.tls.caCert.secretName must be provided if global.tls.enabled=true and global.secretsBackend.vault.enabled=true." }}{{ end -}} +{{- if (and (and .Values.global.secretsBackend.vault.enabled .Values.global.tls.enabled) (not .Values.global.tls.enableAutoEncrypt)) }}{{ fail "global.tls.enableAutoEncrypt must be true if global.secretsBackend.vault.enabled=true and global.tls.enabled=true" }}{{ end -}} +{{- if (and (and .Values.global.secretsBackend.vault.enabled .Values.global.tls.enabled) (not .Values.global.secretsBackend.vault.consulCARole)) }}{{ fail "global.secretsBackend.vault.consulCARole must be provided if global.secretsBackend.vault.enabled=true and global.tls.enabled=true" }}{{ end -}} +{{- if and .Values.global.federation.enabled .Values.global.adminPartitions.enabled }}{{ fail "If global.federation.enabled is true, global.adminPartitions.enabled must be false because they are mutually exclusive" }}{{ end }} +{{- if (and .Values.global.enterpriseLicense.secretName (not .Values.global.enterpriseLicense.secretKey)) }}{{fail "enterpriseLicense.secretKey and secretName must both be specified." }}{{ end -}} +{{- if (and (not .Values.global.enterpriseLicense.secretName) .Values.global.enterpriseLicense.secretKey) }}{{fail "enterpriseLicense.secretKey and secretName must both be specified." }}{{ end -}} +{{- if and .Values.externalServers.enabled (not .Values.externalServers.hosts) }}{{ fail "externalServers.hosts must be set if externalServers.enabled is true" }}{{ end -}} +{{ template "consul.validateRequiredCloudSecretsExist" . }} +{{ template "consul.validateCloudSecretKeys" . }} +# DaemonSet to run the Consul clients on every node. +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: {{ template "consul.fullname" . }}-client + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: client + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} +spec: + {{- if .Values.client.updateStrategy }} + updateStrategy: + {{ tpl .Values.client.updateStrategy . | nindent 4 | trim }} + {{- end }} + selector: + matchLabels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: client + hasDNS: "true" + template: + metadata: + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: client + hasDNS: "true" + {{- if .Values.client.extraLabels }} + {{- toYaml .Values.client.extraLabels | nindent 8 }} + {{- end }} + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} + annotations: + {{- if .Values.global.secretsBackend.vault.enabled }} + "vault.hashicorp.com/agent-inject": "true" + "vault.hashicorp.com/agent-init-first": "true" + "vault.hashicorp.com/role": "{{ .Values.global.secretsBackend.vault.consulClientRole }}" + {{- if and .Values.global.secretsBackend.vault.ca.secretName .Values.global.secretsBackend.vault.ca.secretKey }} + "vault.hashicorp.com/agent-extra-secret": "{{ .Values.global.secretsBackend.vault.ca.secretName }}" + "vault.hashicorp.com/ca-cert": "/vault/custom/{{ .Values.global.secretsBackend.vault.ca.secretKey }}" + {{- end }} + {{- if .Values.global.gossipEncryption.secretName }} + {{- with .Values.global.gossipEncryption }} + "vault.hashicorp.com/agent-inject-secret-gossip.txt": {{ .secretName }} + "vault.hashicorp.com/agent-inject-template-gossip.txt": {{ template "consul.vaultSecretTemplate" . }} + {{- end }} + {{- end }} + {{- if .Values.global.tls.enabled }} + "vault.hashicorp.com/agent-inject-secret-serverca.crt": {{ .Values.global.tls.caCert.secretName }} + "vault.hashicorp.com/agent-inject-template-serverca.crt": {{ template "consul.serverTLSCATemplate" . }} + {{- end }} + {{- if .Values.global.secretsBackend.vault.agentAnnotations }} + {{ tpl .Values.global.secretsBackend.vault.agentAnnotations . | nindent 8 | trim }} + {{- end }} + {{- if (and (.Values.global.secretsBackend.vault.vaultNamespace) (not (hasKey (default "" .Values.global.secretsBackend.vault.agentAnnotations | fromYaml) "vault.hashicorp.com/namespace")))}} + "vault.hashicorp.com/namespace": "{{ .Values.global.secretsBackend.vault.vaultNamespace }}" + {{- end }} + {{- if and .Values.global.enterpriseLicense.secretName (not .Values.global.acls.manageSystemACLs) }} + {{- with .Values.global.enterpriseLicense }} + "vault.hashicorp.com/agent-inject-secret-enterpriselicense.txt": "{{ .secretName }}" + "vault.hashicorp.com/agent-inject-template-enterpriselicense.txt": {{ template "consul.vaultSecretTemplate" . }} + {{- end }} + {{- end }} + {{- end }} + "consul.hashicorp.com/connect-inject": "false" + "consul.hashicorp.com/mesh-inject": "false" + "consul.hashicorp.com/config-checksum": {{ print (include (print $.Template.BasePath "/client-config-configmap.yaml") .) (include (print $.Template.BasePath "/client-tmp-extra-config-configmap.yaml") .) | sha256sum }} + {{- if .Values.client.annotations }} + {{- tpl .Values.client.annotations . | nindent 8 }} + {{- end }} + {{- if (and .Values.global.metrics.enabled .Values.global.metrics.enableAgentMetrics) }} + "prometheus.io/scrape": "true" + {{- if not (hasKey (default "" .Values.client.annotations | fromYaml) "prometheus.io/path")}} + "prometheus.io/path": "/v1/agent/metrics" + {{- end }} + "prometheus.io/port": "8500" + {{- end }} + spec: + {{- if .Values.client.affinity }} + affinity: + {{ tpl .Values.client.affinity . | nindent 8 | trim }} + {{- end }} + {{- if .Values.client.tolerations }} + tolerations: + {{ tpl .Values.client.tolerations . | nindent 8 | trim }} + {{- end }} + terminationGracePeriodSeconds: 10 + serviceAccountName: {{ template "consul.fullname" . }}-client + + {{- if not .Values.global.openshift.enabled }} + securityContext: + {{- toYaml .Values.client.securityContext | nindent 8 -}} + {{- end }} + + {{- if .Values.client.priorityClassName }} + priorityClassName: {{ .Values.client.priorityClassName | quote }} + {{- end }} + + {{- if .Values.client.dnsPolicy }} + dnsPolicy: {{ .Values.client.dnsPolicy }} + {{- end }} + + {{- if .Values.client.hostNetwork }} + hostNetwork: {{ .Values.client.hostNetwork }} + {{- end }} + + volumes: + - name: data + {{- if .Values.client.dataDirectoryHostPath }} + hostPath: + path: {{ .Values.client.dataDirectoryHostPath }} + type: DirectoryOrCreate + {{- else }} + emptyDir: {} + {{- end }} + - name: config + configMap: + name: {{ template "consul.fullname" . }}-client-config + - name: extra-config + emptyDir: {} + - name: consul-data + emptyDir: + medium: "Memory" + - name: tmp-extra-config + configMap: + name: {{ template "consul.fullname" . }}-client-tmp-extra-config + {{- if .Values.global.tls.enabled }} + {{- if not .Values.global.secretsBackend.vault.enabled }} + - name: consul-ca-cert + secret: + {{- if .Values.global.tls.caCert.secretName }} + secretName: {{ .Values.global.tls.caCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" . }}-ca-cert + {{- end }} + items: + - key: {{ default "tls.crt" .Values.global.tls.caCert.secretKey }} + path: tls.crt + {{- end }} + {{- if (and (not .Values.global.secretsBackend.vault.enabled) (not .Values.global.tls.enableAutoEncrypt)) }} + - name: consul-ca-key + secret: + {{- if .Values.global.tls.caKey.secretName }} + secretName: {{ .Values.global.tls.caKey.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" . }}-ca-key + {{- end }} + items: + - key: {{ default "tls.key" .Values.global.tls.caKey.secretKey }} + path: tls.key + - name: consul-client-cert + emptyDir: + # We're using tmpfs here so that + # client certs are not written to disk + medium: "Memory" + {{- end }} + {{- end }} + {{- range .Values.client.extraVolumes }} + - name: userconfig-{{ .name }} + {{ .type }}: + {{- if (eq .type "configMap") }} + name: {{ .name }} + {{- else if (eq .type "secret") }} + secretName: {{ .name }} + {{- end }} + {{- end }} + {{- if .Values.global.acls.manageSystemACLs }} + - name: aclconfig + emptyDir: {} + {{- else }} + {{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey .Values.global.enterpriseLicense.enableLicenseAutoload (not .Values.global.secretsBackend.vault.enabled)) }} + - name: consul-license + secret: + secretName: {{ .Values.global.enterpriseLicense.secretName }} + {{- end }} + {{- end }} + containers: + - name: consul + image: "{{ default .Values.global.image .Values.client.image }}" + {{ template "consul.imagePullPolicy" . }} + {{- if .Values.global.acls.manageSystemACLs }} + lifecycle: + preStop: + exec: + command: + - "/bin/consul" + - "logout" + {{- end }} + env: + {{- if .Values.global.acls.manageSystemACLs }} + - name: CONSUL_HTTP_TOKEN_FILE + value: "/consul/login/acl-token" + {{- end }} + - name: ADVERTISE_IP + valueFrom: + fieldRef: + {{- if .Values.client.exposeGossipPorts }} + {{- /* Clients will be exposed on their node's hostPort for external-to-k8s communication, + so they need to advertise their host ip instead of their pod ip. */}} + fieldPath: status.hostIP + {{- else }} + fieldPath: status.podIP + {{- end }} + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: NODE + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: CONSUL_DISABLE_PERM_MGMT + value: "true" + {{- if (or .Values.global.gossipEncryption.autoGenerate (and .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey)) }} + {{- if not .Values.global.secretsBackend.vault.enabled }} + - name: GOSSIP_KEY + valueFrom: + secretKeyRef: + {{- if .Values.global.gossipEncryption.autoGenerate }} + name: {{ template "consul.fullname" . }}-gossip-encryption-key + key: key + {{- else if (and .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey) }} + name: {{ .Values.global.gossipEncryption.secretName }} + key: {{ .Values.global.gossipEncryption.secretKey }} + {{- end }} + {{- end }} + {{- end }} + {{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey .Values.global.enterpriseLicense.enableLicenseAutoload (not .Values.global.acls.manageSystemACLs)) }} + - name: CONSUL_LICENSE_PATH + {{- if .Values.global.secretsBackend.vault.enabled }} + value: /vault/secrets/enterpriselicense.txt + {{- else }} + value: /consul/license/{{ .Values.global.enterpriseLicense.secretKey }} + {{- end }} + {{- end }} + {{- if .Values.global.tls.enabled }} + - name: CONSUL_HTTP_ADDR + value: https://localhost:8501 + {{- if .Values.global.tls.enableAutoEncrypt }} + - name: CONSUL_HTTP_SSL_VERIFY + value: "false" + {{- else }} + - name: CONSUL_CACERT + value: /consul/tls/ca/tls.crt + {{- end }} + {{- end }} + {{- include "consul.extraEnvironmentVars" .Values.client | nindent 12 }} + command: + - "/bin/sh" + - "-ec" + - | + CONSUL_FULLNAME="{{template "consul.fullname" . }}" + + {{- if and .Values.global.secretsBackend.vault.enabled .Values.global.gossipEncryption.secretName }} + GOSSIP_KEY=`cat /vault/secrets/gossip.txt` + {{- end }} + + {{ template "consul.extraconfig" }} + + exec /usr/local/bin/docker-entrypoint.sh consul agent \ + -node="${NODE}" \ + -advertise="${ADVERTISE_IP}" \ + -bind=0.0.0.0 \ + -client=0.0.0.0 \ + {{- range $k, $v := .Values.client.nodeMeta }} + -node-meta={{ $k }}:{{ $v }} \ + {{- end }} + -hcl='leave_on_terminate = true' \ + {{- if .Values.global.tls.enabled }} + {{- if .Values.global.peering.enabled }} + {{- if .Values.global.secretsBackend.vault.enabled }} + -hcl='tls { defaults { ca_file = "/vault/secrets/serverca.crt" }}' \ + {{- else }} + -hcl='tls { defaults { ca_file = "/consul/tls/ca/tls.crt" }}' \ + {{- end }} + {{- if .Values.global.tls.enableAutoEncrypt }} + -hcl='auto_encrypt = {tls = true}' \ + -hcl="auto_encrypt = {ip_san = [\"$HOST_IP\",\"$POD_IP\"]}" \ + -hcl='tls { grpc { use_auto_cert = true } }' \ + {{- else }} + -hcl='tls { defaults { cert_file = "/consul/tls/client/tls.crt" }}' \ + -hcl='tls { defaults { key_file = "/consul/tls/client/tls.key" }}' \ + {{- end }} + {{- if .Values.global.tls.verify }} + -hcl='tls { defaults { verify_outgoing = true }}' \ + {{- if not .Values.global.tls.enableAutoEncrypt }} + -hcl='tls { internal_rpc { verify_incoming = true }}' \ + -hcl='tls { internal_rpc { verify_server_hostname = true }}' \ + {{- end }} + {{- end }} + -hcl='ports { https = 8501 }' \ + {{- if .Values.global.tls.httpsOnly }} + -hcl='ports { http = -1 }' \ + {{- end }} + {{- else}} + {{- if .Values.global.secretsBackend.vault.enabled }} + -hcl='ca_file = "/vault/secrets/serverca.crt"' \ + {{- else }} + -hcl='ca_file = "/consul/tls/ca/tls.crt"' \ + {{- end }} + {{- if .Values.global.tls.enableAutoEncrypt }} + -hcl='auto_encrypt = {tls = true}' \ + -hcl="auto_encrypt = {ip_san = [\"$HOST_IP\",\"$POD_IP\"]}" \ + {{- else }} + -hcl='cert_file = "/consul/tls/client/tls.crt"' \ + -hcl='key_file = "/consul/tls/client/tls.key"' \ + {{- end }} + {{- if .Values.global.tls.verify }} + -hcl='verify_outgoing = true' \ + {{- if not .Values.global.tls.enableAutoEncrypt }} + -hcl='verify_incoming_rpc = true' \ + -hcl='verify_server_hostname = true' \ + {{- end }} + {{- end }} + -hcl='ports { https = 8501 }' \ + {{- if .Values.global.tls.httpsOnly }} + -hcl='ports { http = -1 }' \ + {{- end }} + {{- end }} + {{- end }} + {{- if .Values.client.grpc }} + {{- if .Values.global.tls.enabled }} + -hcl='ports { grpc = -1, grpc_tls = 8502 }' \ + {{- else }} + -hcl='ports { grpc = 8502, grpc_tls = -1 }' \ + {{- end }} + {{- end }} + {{- if (and .Values.global.metrics.enabled .Values.global.metrics.enableAgentMetrics) }} + -hcl='telemetry { prometheus_retention_time = "{{ .Values.global.metrics.agentMetricsRetentionTime }}" }' \ + {{- end }} + {{- if .Values.global.adminPartitions.enabled }} + -hcl='partition = "{{ .Values.global.adminPartitions.name }}"' \ + {{- end }} + -config-dir=/consul/config \ + {{- if .Values.global.acls.manageSystemACLs }} + -config-dir=/consul/aclconfig \ + {{- end }} + {{- /* Always include the extraVolumes at the end so that users can + override other Consul settings. The last -config-dir takes + precedence. */}} + {{- range .Values.client.extraVolumes }} + {{- if .load }} + -config-dir=/consul/userconfig/{{ .name }} \ + {{- end }} + {{- end }} + -datacenter={{ .Values.global.datacenter }} \ + -data-dir=/consul/data \ + {{- if (or .Values.global.gossipEncryption.autoGenerate (and .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey)) }} + -encrypt="${GOSSIP_KEY}" \ + {{- end }} + {{- if .Values.client.join }} + {{- range $value := .Values.client.join }} + -retry-join={{ quote $value }} \ + {{- end }} + {{- else }} + {{- if .Values.server.enabled }} + {{- $serverSerfLANPort := .Values.server.ports.serflan.port -}} + {{- range $index := until (.Values.server.replicas | int) }} + -retry-join="${CONSUL_FULLNAME}-server-{{ $index }}.${CONSUL_FULLNAME}-server.${NAMESPACE}.svc:{{ $serverSerfLANPort }}" \ + {{- end }} + {{- end }} + {{- end }} + {{- range $value := .Values.global.recursors }} + -recursor={{ quote $value }} \ + {{- end }} + -config-dir=/consul/extra-config \ + -domain={{ .Values.global.domain }} + volumeMounts: + - name: data + mountPath: /consul/data + - name: config + mountPath: /consul/config + - name: extra-config + mountPath: /consul/extra-config + - name: tmp-extra-config + mountPath: /consul/tmp/extra-config + - mountPath: /consul/login + name: consul-data + readOnly: true + {{- if .Values.global.tls.enabled }} + {{- if not .Values.global.secretsBackend.vault.enabled }} + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: true + {{- if not .Values.global.tls.enableAutoEncrypt }} + - name: consul-client-cert + mountPath: /consul/tls/client + readOnly: true + {{- end }} + {{- end }} + {{- end }} + {{- range .Values.client.extraVolumes }} + - name: userconfig-{{ .name }} + readOnly: true + mountPath: /consul/userconfig/{{ .name }} + {{- end }} + {{- if .Values.global.acls.manageSystemACLs }} + - name: aclconfig + mountPath: /consul/aclconfig + {{- else }} + {{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey .Values.global.enterpriseLicense.enableLicenseAutoload (not .Values.global.secretsBackend.vault.enabled)) }} + - name: consul-license + mountPath: /consul/license + readOnly: true + {{- end }} + {{- end }} + ports: + {{- if (or (not .Values.global.tls.enabled) (not .Values.global.tls.httpsOnly)) }} + - containerPort: 8500 + hostPort: 8500 + name: http + {{- end }} + {{- if .Values.global.tls.enabled }} + - containerPort: 8501 + hostPort: 8501 + name: https + {{- end }} + - containerPort: 8502 + hostPort: 8502 + name: grpc + - containerPort: 8301 + {{- if .Values.client.exposeGossipPorts }} + hostPort: 8301 + {{- end }} + protocol: "TCP" + name: serflan-tcp + - containerPort: 8301 + {{- if .Values.client.exposeGossipPorts }} + hostPort: 8301 + {{- end }} + protocol: "UDP" + name: serflan-udp + - containerPort: 8600 + name: dns-tcp + protocol: "TCP" + - containerPort: 8600 + name: dns-udp + protocol: "UDP" + readinessProbe: + # NOTE(mitchellh): when our HTTP status endpoints support the + # proper status codes, we should switch to that. This is temporary. + exec: + command: + - "/bin/sh" + - "-ec" + - | + {{- if .Values.global.tls.enabled }} + curl \ + -k \ + https://127.0.0.1:8501/v1/status/leader \ + {{- else }} + curl http://127.0.0.1:8500/v1/status/leader \ + {{- end }} + 2>/dev/null | grep -E '".+"' + {{- if .Values.client.resources }} + resources: + {{- if eq (typeOf .Values.client.resources) "string" }} + {{ tpl .Values.client.resources . | nindent 12 | trim }} + {{- else }} + {{- toYaml .Values.client.resources | nindent 12 }} + {{- end }} + {{- end }} + {{- if not .Values.global.openshift.enabled }} + securityContext: + {{- toYaml .Values.client.containerSecurityContext.client | nindent 12 }} + {{- end }} + {{- if .Values.client.extraContainers }} + {{ toYaml .Values.client.extraContainers | nindent 8 }} + {{- end }} + {{- if (or .Values.global.acls.manageSystemACLs (and .Values.global.tls.enabled (not .Values.global.tls.enableAutoEncrypt))) }} + initContainers: + {{- if .Values.global.acls.manageSystemACLs }} + - name: client-acl-init + image: {{ .Values.global.imageK8S }} + {{ template "consul.imagePullPolicy" . }} + env: + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + {{- include "consul.consulK8sConsulServerEnvVars" . | nindent 8 }} + {{- if .Values.global.acls.manageSystemACLs }} + - name: CONSUL_LOGIN_AUTH_METHOD + value: {{ template "consul.fullname" . }}-k8s-component-auth-method + - name: CONSUL_LOGIN_META + value: "component=client,pod=$(NAMESPACE)/$(POD_NAME)" + {{- end }} + - name: CONSUL_LOGIN_DATACENTER + value: {{ .Values.global.datacenter }} + command: + - "/bin/sh" + - "-ec" + - | + exec consul-k8s-control-plane acl-init \ + -log-level={{ default .Values.global.logLevel .Values.client.logLevel }} \ + -log-json={{ .Values.global.logJSON }} \ + -init-type="client" + volumeMounts: + - name: aclconfig + mountPath: /consul/aclconfig + - mountPath: /consul/login + name: consul-data + readOnly: false + {{- if (and .Values.global.tls.enabled (not .Values.global.secretsBackend.vault.enabled) (not .Values.externalServers.useSystemRoots)) }} + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: false + {{- end }} + resources: + requests: + memory: "25Mi" + cpu: "50m" + limits: + memory: "25Mi" + cpu: "50m" + {{- if not .Values.global.openshift.enabled }} + securityContext: + {{- toYaml .Values.client.containerSecurityContext.aclInit | nindent 10 }} + {{- end }} + {{- end }} + {{- if and .Values.global.tls.enabled (not .Values.global.tls.enableAutoEncrypt) }} + - name: client-tls-init + image: "{{ default .Values.global.image .Values.client.image }}" + {{ template "consul.imagePullPolicy" . }} + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + command: + - "/bin/sh" + - "-ec" + - | + cd /consul/tls/client + consul tls cert create -client \ + -additional-ipaddress=${HOST_IP} \ + -additional-ipaddress=${POD_IP} \ + -dc={{ .Values.global.datacenter }} \ + -domain={{ .Values.global.domain }} \ + -ca=/consul/tls/ca/cert/tls.crt \ + -key=/consul/tls/ca/key/tls.key + mv {{ .Values.global.datacenter }}-client-{{ .Values.global.domain }}-0.pem tls.crt + mv {{ .Values.global.datacenter }}-client-{{ .Values.global.domain }}-0-key.pem tls.key + volumeMounts: + {{- if not .Values.global.secretsBackend.vault.enabled }} + - name: consul-client-cert + mountPath: /consul/tls/client + - name: consul-ca-cert + mountPath: /consul/tls/ca/cert + readOnly: true + - name: consul-ca-key + mountPath: /consul/tls/ca/key + readOnly: true + {{- end }} + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" + {{- if not .Values.global.openshift.enabled }} + securityContext: + {{- toYaml .Values.client.containerSecurityContext.tlsInit | nindent 10 }} + {{- end }} + {{- end }} + {{- end }} + {{- if .Values.client.nodeSelector }} + nodeSelector: + {{ tpl .Values.client.nodeSelector . | indent 8 | trim }} + {{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/client-podsecuritypolicy.yaml b/charts/hashicorp/consul/1.5.3/templates/client-podsecuritypolicy.yaml new file mode 100644 index 0000000000..0121bdf586 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/client-podsecuritypolicy.yaml @@ -0,0 +1,76 @@ +{{- if (and .Values.global.enablePodSecurityPolicies (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled))) }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-client + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: client +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + # Allow core volume types. + volumes: + - 'configMap' + - 'emptyDir' + - 'projected' + - 'secret' + - 'downwardAPI' + {{- if .Values.client.dataDirectoryHostPath }} + - 'hostPath' + {{- end }} + {{- if .Values.client.hostNetwork }} + hostNetwork: {{ .Values.client.hostNetwork }} + {{- else }} + hostNetwork: false + {{- end }} + hostPorts: + {{- if (not (and .Values.global.tls.enabled .Values.global.tls.httpsOnly)) }} + # HTTP Port + - min: 8500 + max: 8500 + {{- end }} + {{- if .Values.global.tls.enabled }} + # HTTPS port + - min: 8501 + max: 8501 + {{- end }} + {{- if .Values.client.grpc }} + # gRPC Port + - min: 8502 + max: 8502 + {{- end }} + {{- if (or .Values.client.exposeGossipPorts .Values.client.hostNetwork) }} + - min: 8301 + max: 8301 + {{- end }} + {{- if .Values.client.hostNetwork }} + - min: 8600 + max: 8600 + {{- end }} + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false + {{- if .Values.client.dataDirectoryHostPath }} + allowedHostPaths: + - pathPrefix: {{ .Values.client.dataDirectoryHostPath | quote }} + readOnly: false + {{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/client-role.yaml b/charts/hashicorp/consul/1.5.3/templates/client-role.yaml new file mode 100644 index 0000000000..7f05b82e6b --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/client-role.yaml @@ -0,0 +1,43 @@ +{{- if (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" . }}-client + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: client +{{- if (or .Values.global.acls.manageSystemACLs .Values.global.enablePodSecurityPolicies .Values.global.openshift.enabled) }} +rules: +{{- if .Values.global.enablePodSecurityPolicies }} + - apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + resourceNames: + - {{ template "consul.fullname" . }}-client + verbs: + - use +{{- end }} +{{- if .Values.global.acls.manageSystemACLs }} + - apiGroups: [""] + resources: + - secrets + resourceNames: + - {{ template "consul.fullname" . }}-client-acl-token + verbs: + - get +{{- end }} +{{- if .Values.global.openshift.enabled}} + - apiGroups: ["security.openshift.io"] + resources: ["securitycontextconstraints"] + resourceNames: + - {{ template "consul.fullname" . }}-client + verbs: + - use +{{- end}} +{{- else}} +rules: [] +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/client-rolebinding.yaml b/charts/hashicorp/consul/1.5.3/templates/client-rolebinding.yaml new file mode 100644 index 0000000000..b034c70e55 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/client-rolebinding.yaml @@ -0,0 +1,20 @@ +{{- if (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" . }}-client + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: client +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" . }}-client +subjects: + - kind: ServiceAccount + name: {{ template "consul.fullname" . }}-client +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/client-securitycontextconstraints.yaml b/charts/hashicorp/consul/1.5.3/templates/client-securitycontextconstraints.yaml new file mode 100644 index 0000000000..c14dd1c991 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/client-securitycontextconstraints.yaml @@ -0,0 +1,61 @@ +{{- if (and .Values.global.openshift.enabled (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled))) }} +apiVersion: security.openshift.io/v1 +kind: SecurityContextConstraints +metadata: + name: {{ template "consul.fullname" . }}-client + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: client + annotations: + kubernetes.io/description: {{ template "consul.fullname" . }}-client are the security context constraints required + to run the consul client. +# Iff. allowHostDirVolumePlugin is true, hostPath must be included in volumes (see below). +{{- if .Values.client.dataDirectoryHostPath }} +allowHostDirVolumePlugin: true +{{- else }} +allowHostDirVolumePlugin: false +{{- end}} +allowHostIPC: false +allowHostNetwork: {{ .Values.client.hostNetwork }} +allowHostPID: false +allowHostPorts: true +allowPrivilegeEscalation: true +allowPrivilegedContainer: false +allowedCapabilities: null +defaultAddCapabilities: null +fsGroup: + type: MustRunAs +groups: [] +priority: null +readOnlyRootFilesystem: false +requiredDropCapabilities: +- KILL +- MKNOD +- SETUID +- SETGID +runAsUser: + type: MustRunAsRange +seLinuxContext: + type: MustRunAs +supplementalGroups: + type: MustRunAs +users: [] +volumes: +# This list must be in alphabetical order to match the post-reconcile order enforced by OpenShift admission hooks. +# Furthermore, hostPath must be included explicitly if allowHostDirVolumePlugin is true, as it will otherwise be +# added by OpenShift. It must be excluded if allowHostDirVolumePlugin is false per OpenShift requirements. +# This avoids false positives in change detection by third-party diff tools (e.g. ArgoCD) that respect list order. +- configMap +- downwardAPI +- emptyDir +{{- if .Values.client.dataDirectoryHostPath }} +- hostPath +{{- end }} +- persistentVolumeClaim +- projected +- secret +{{- end}} diff --git a/charts/hashicorp/consul/1.5.3/templates/client-serviceaccount.yaml b/charts/hashicorp/consul/1.5.3/templates/client-serviceaccount.yaml new file mode 100644 index 0000000000..addd757b84 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/client-serviceaccount.yaml @@ -0,0 +1,23 @@ +{{- if (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled)) }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-client + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: client + {{- if .Values.client.serviceAccount.annotations }} + annotations: + {{ tpl .Values.client.serviceAccount.annotations . | nindent 4 | trim }} + {{- end }} +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/client-tmp-extra-config-configmap.yaml b/charts/hashicorp/consul/1.5.3/templates/client-tmp-extra-config-configmap.yaml new file mode 100644 index 0000000000..a379157f57 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/client-tmp-extra-config-configmap.yaml @@ -0,0 +1,21 @@ +{{- if (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled)) }} +# ConfigMap that is used as a temporary landing spot so that the container command +# in the client-daemonset where it needs to be transformed. ConfigMaps create +# read only volumes so it needs to be copied and transformed to the extra-config +# emptyDir volume where all final extra cofngi lives for use in consul. (locality-init +# also writes to extra-config volume.) +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "consul.fullname" . }}-client-tmp-extra-config + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: client +data: + extra-from-values.json: |- +{{ tpl .Values.client.extraConfig . | trimAll "\"" | indent 4 }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/cni-clusterrole.yaml b/charts/hashicorp/consul/1.5.3/templates/cni-clusterrole.yaml new file mode 100644 index 0000000000..773942cca8 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/cni-clusterrole.yaml @@ -0,0 +1,38 @@ +{{- if .Values.connectInject.cni.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "consul.fullname" . }}-cni + namespace: {{ default .Release.Namespace .Values.connectInject.cni.namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: cni +rules: +- apiGroups: [""] + resources: + - pods + verbs: + - get + - list + - watch + - patch + - update +- apiGroups: ["policy"] + resources: + - podsecuritypolicies + resourceNames: + - {{ template "consul.fullname" . }}-cni + verbs: + - use +{{- if .Values.global.openshift.enabled}} +- apiGroups: ["security.openshift.io"] + resources: ["securitycontextconstraints"] + resourceNames: + - {{ template "consul.fullname" . }}-cni + verbs: + - use +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/cni-clusterrolebinding.yaml b/charts/hashicorp/consul/1.5.3/templates/cni-clusterrolebinding.yaml new file mode 100644 index 0000000000..4b860388b6 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/cni-clusterrolebinding.yaml @@ -0,0 +1,20 @@ +{{- if .Values.connectInject.cni.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "consul.fullname" . }}-cni + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: cni +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "consul.fullname" . }}-cni +subjects: +- kind: ServiceAccount + name: {{ template "consul.fullname" . }}-cni + namespace: {{ default .Release.Namespace .Values.connectInject.cni.namespace }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/cni-daemonset.yaml b/charts/hashicorp/consul/1.5.3/templates/cni-daemonset.yaml new file mode 100644 index 0000000000..a93e3aea90 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/cni-daemonset.yaml @@ -0,0 +1,92 @@ +{{- if (and (.Values.connectInject.cni.enabled) (not .Values.connectInject.enabled)) }}{{ fail "connectInject.enabled must be true if connectInject.cni.enabled is true" }}{{ end -}} +{{- if .Values.connectInject.cni.enabled }} +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: {{ template "consul.fullname" . }}-cni + namespace: {{ default .Release.Namespace .Values.connectInject.cni.namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: cni + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} +spec: + {{- if .Values.connectInject.cni.updateStrategy }} + updateStrategy: + {{ tpl .Values.connectInject.cni.updateStrategy . | nindent 4 | trim }} + {{- end }} + selector: + matchLabels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: cni + template: + metadata: + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: cni + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} + annotations: + consul.hashicorp.com/connect-inject: "false" + consul.hashicorp.com/mesh-inject: "false" + spec: + # consul-cni only runs on linux operating systems + nodeSelector: + kubernetes.io/os: linux + tolerations: + # Mark the pod as a critical add-on for rescheduling. + - key: CriticalAddonsOnly + operator: Exists + - effect: NoExecute + operator: Exists + # Tell kubernetes that this daemonset is critical so that it will be scheduled on a new node before other pods + priorityClassName: system-node-critical + serviceAccountName: {{ template "consul.fullname" . }}-cni + {{- if not .Values.global.openshift.enabled }} + securityContext: + {{- toYaml .Values.connectInject.cni.securityContext | nindent 8 -}} + {{- end }} + # Minimize downtime during a rolling upgrade or deletion; tell Kubernetes to do a "force + # deletion": https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods. + terminationGracePeriodSeconds: 10 + containers: + # This container installs the consul CNI binaries and CNI network config file on each node + - name: install-cni + image: {{ .Values.global.imageK8S }} + {{ template "consul.imagePullPolicy" . }} + securityContext: + privileged: true + command: + - consul-k8s-control-plane + - install-cni + - -log-level={{ default .Values.global.logLevel .Values.connectInject.cni.logLevel }} + - -cni-bin-dir={{ .Values.connectInject.cni.cniBinDir }} + - -cni-net-dir={{ .Values.connectInject.cni.cniNetDir }} + - -multus={{ .Values.connectInject.cni.multus }} + {{- with .Values.connectInject.cni.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + - mountPath: {{ .Values.connectInject.cni.cniBinDir }} + name: cni-bin-dir + - mountPath: {{ .Values.connectInject.cni.cniNetDir }} + name: cni-net-dir + volumes: + # Used to install CNI. + - name: cni-bin-dir + hostPath: + path: {{ .Values.connectInject.cni.cniBinDir }} + - name: cni-net-dir + hostPath: + path: {{ .Values.connectInject.cni.cniNetDir }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/cni-networkattachmentdefinition.yaml b/charts/hashicorp/consul/1.5.3/templates/cni-networkattachmentdefinition.yaml new file mode 100644 index 0000000000..80ef50bac6 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/cni-networkattachmentdefinition.yaml @@ -0,0 +1,25 @@ +{{- if (and (.Values.connectInject.cni.enabled) (.Values.connectInject.cni.multus)) }} +apiVersion: "k8s.cni.cncf.io/v1" +kind: NetworkAttachmentDefinition +metadata: + name: {{ template "consul.fullname" . }}-cni + namespace: {{ default .Release.Namespace .Values.connectInject.cni.namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: cni +spec: + config: '{ + "cniVersion": "0.3.1", + "type": "consul-cni", + "cni_bin_dir": "{{ .Values.connectInject.cni.cniBinDir }}", + "cni_net_dir": "{{ .Values.connectInject.cni.cniNetDir }}", + "kubeconfig": "ZZZ-consul-cni-kubeconfig", + "log_level": "{{ default .Values.global.logLevel .Values.connectInject.cni.logLevel }}", + "multus": true, + "name": "consul-cni", + "type": "consul-cni" + }' +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/cni-podsecuritypolicy.yaml b/charts/hashicorp/consul/1.5.3/templates/cni-podsecuritypolicy.yaml new file mode 100644 index 0000000000..b600ed1b4b --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/cni-podsecuritypolicy.yaml @@ -0,0 +1,31 @@ +{{- if (and .Values.connectInject.cni.enabled .Values.global.enablePodSecurityPolicies) }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-cni + namespace: {{ default .Release.Namespace .Values.connectInject.cni.namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: cni +spec: + privileged: true + # GKE requires that allowPrivilegeEscalation:true if privileged: true. + allowPrivilegeEscalation: true + volumes: + - hostPath + - secret + - emptyDir + hostNetwork: false + readOnlyRootFilesystem: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/cni-resourcequota.yaml b/charts/hashicorp/consul/1.5.3/templates/cni-resourcequota.yaml new file mode 100644 index 0000000000..054c3061f5 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/cni-resourcequota.yaml @@ -0,0 +1,22 @@ +{{- if .Values.connectInject.cni.enabled }} +apiVersion: v1 +kind: ResourceQuota +metadata: + name: {{ template "consul.fullname" . }}-cni + namespace: {{ default .Release.Namespace .Values.connectInject.cni.namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: cni +spec: + hard: + pods: {{ .Values.connectInject.cni.resourceQuota.pods | quote }} + scopeSelector: + matchExpressions: + - operator: In + scopeName: PriorityClass + values: + - system-node-critical +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/cni-securitycontextconstraints.yaml b/charts/hashicorp/consul/1.5.3/templates/cni-securitycontextconstraints.yaml new file mode 100644 index 0000000000..cb60104cf0 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/cni-securitycontextconstraints.yaml @@ -0,0 +1,55 @@ +{{- if (and (.Values.connectInject.cni.enabled) (.Values.global.openshift.enabled)) }} +apiVersion: security.openshift.io/v1 +kind: SecurityContextConstraints +metadata: + name: {{ template "consul.fullname" . }}-cni + namespace: {{ default .Release.Namespace .Values.connectInject.cni.namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: cni + annotations: + kubernetes.io/description: {{ template "consul.fullname" . }}-cni are the security context constraints required + to run consul-cni. +# Iff. allowHostDirVolumePlugin is true, hostPath must be included in volumes (see below). +allowHostDirVolumePlugin: true +allowHostIPC: false +allowHostNetwork: false +allowHostPID: false +allowHostPorts: false +allowPrivilegeEscalation: true +allowPrivilegedContainer: true +allowedCapabilities: null +defaultAddCapabilities: null +fsGroup: + type: MustRunAs +groups: [] +priority: null +readOnlyRootFilesystem: false +requiredDropCapabilities: +- KILL +- MKNOD +- SETUID +- SETGID +runAsUser: + type: MustRunAsRange +seLinuxContext: + type: MustRunAs +supplementalGroups: + type: MustRunAs +users: [] +volumes: +# This list must be in alphabetical order to match the post-reconcile order enforced by OpenShift admission hooks. +# Furthermore, hostPath must be included explicitly if allowHostDirVolumePlugin is true, as it will otherwise be +# added by OpenShift. It must be excluded if allowHostDirVolumePlugin is false per OpenShift requirements. +# This avoids false positives in change detection by third-party diff tools (e.g. ArgoCD) that respect list order. +- configMap +- downwardAPI +- emptyDir +- hostPath +- persistentVolumeClaim +- projected +- secret +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/cni-serviceaccount.yaml b/charts/hashicorp/consul/1.5.3/templates/cni-serviceaccount.yaml new file mode 100644 index 0000000000..cf4250b696 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/cni-serviceaccount.yaml @@ -0,0 +1,19 @@ +{{- if .Values.connectInject.cni.enabled }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-cni + namespace: {{ default .Release.Namespace .Values.connectInject.cni.namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: cni +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} +- name: {{ .name }} +{{- end }} +{{- end }} +{{- end}} diff --git a/charts/hashicorp/consul/1.5.3/templates/connect-inject-clusterrole.yaml b/charts/hashicorp/consul/1.5.3/templates/connect-inject-clusterrole.yaml new file mode 100644 index 0000000000..9c8596b05b --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/connect-inject-clusterrole.yaml @@ -0,0 +1,291 @@ +{{- if or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled) }} +# The ClusterRole to enable the Connect injector to get, list, watch and patch MutatingWebhookConfiguration. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "consul.fullname" . }}-connect-injector + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: connect-injector +rules: +- apiGroups: + - consul.hashicorp.com + resources: + - servicedefaults + - serviceresolvers + - proxydefaults + - meshes + - exportedservices + - servicerouters + - servicesplitters + - serviceintentions + - ingressgateways + - terminatinggateways + - gatewayclassconfigs + - meshservices + - samenessgroups + - controlplanerequestlimits + - routeretryfilters + - routetimeoutfilters + - routeauthfilters + - gatewaypolicies + - registrations + {{- if .Values.global.peering.enabled }} + - peeringacceptors + - peeringdialers + {{- end }} + - jwtproviders + - routeauthfilters + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - consul.hashicorp.com + resources: + - servicedefaults/status + - serviceresolvers/status + - proxydefaults/status + - meshes/status + - exportedservices/status + - servicerouters/status + - servicesplitters/status + - serviceintentions/status + - ingressgateways/status + - terminatinggateways/status + - samenessgroups/status + - controlplanerequestlimits/status + - registrations/status + {{- if .Values.global.peering.enabled }} + - peeringacceptors/status + - peeringdialers/status + {{- end }} + - jwtproviders/status + - routeauthfilters/status + - gatewaypolicies/status + verbs: + - get + - patch + - update +{{- if (mustHas "resource-apis" .Values.global.experiments) }} +- apiGroups: + - auth.consul.hashicorp.com + resources: + - trafficpermissions + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - auth.consul.hashicorp.com + resources: + - trafficpermissions/status + verbs: + - get + - patch + - update +- apiGroups: + - mesh.consul.hashicorp.com + resources: + - gatewayclassconfigs + - gatewayclasses + - meshconfigurations + - grpcroutes + - httproutes + - meshgateways + - apigateways + - tcproutes + - proxyconfigurations + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - mesh.consul.hashicorp.com + resources: + - gatewayclassconfigs/status + - gatewayclasses/status + - meshconfigurations/status + - grpcroutes/status + - httproutes/status + - meshgateways/status + - apigateways/status + - tcproutes/status + - proxyconfigurations/status + verbs: + - get + - patch + - update +- apiGroups: + - multicluster.consul.hashicorp.com + resources: + - exportedservices + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - multicluster.consul.hashicorp.com + resources: + - exportedservices/status + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +{{- end }} +- apiGroups: [ "" ] + resources: [ "secrets", "serviceaccounts", "endpoints", "services", "namespaces", "nodes" ] + verbs: + - create + - get + - list + - watch + - delete + - update +- apiGroups: [ "rbac.authorization.k8s.io" ] + resources: [ "roles", "rolebindings" ] + verbs: + - get + - list + - watch + - delete + - create + - update +- apiGroups: [ "" ] + resources: + - pods + verbs: + - "get" + - "list" + - "watch" + - "update" +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - get + - list + - update +{{- if (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName)}} +- apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + - validatingwebhookconfigurations + verbs: + - get + - list + - watch + - patch +{{- end }} +{{- if .Values.global.peering.enabled }} +- apiGroups: [ "" ] + resources: [ "secrets" ] + verbs: + - "get" + - "list" + - "watch" + - "create" + - "update" + - "delete" +{{- end }} +- apiGroups: [ "policy" ] + resources: [ "podsecuritypolicies" ] + verbs: + - use +- apiGroups: + - gateway.networking.k8s.io + resources: + - gatewayclasses + - gateways + - httproutes + - tcproutes + - referencegrants + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - gatewayclasses/finalizers + - gateways/finalizers + - httproutes/finalizers + - tcproutes/finalizers + verbs: + - update +- apiGroups: + - gateway.networking.k8s.io + resources: + - gatewayclasses/status + - gateways/status + - httproutes/status + - tcproutes/status + verbs: + - get + - patch + - update +- apiGroups: + - apps + resources: + - deployments + verbs: + - create + - get + - list + - update + - watch + - delete +- apiGroups: + - core + resources: + - services + verbs: + - watch + - list +- apiGroups: [ "" ] + resources: [ "secrets" ] + verbs: + - "get" + - "list" + - "watch" +{{- if .Values.global.openshift.enabled }} +- apiGroups: + - security.openshift.io + resources: + - securitycontextconstraints + resourceNames: + - {{ .Values.connectInject.apiGateway.managedGatewayClass.openshiftSCCName }} + verbs: + - use + {{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/connect-inject-clusterrolebinding.yaml b/charts/hashicorp/consul/1.5.3/templates/connect-inject-clusterrolebinding.yaml new file mode 100644 index 0000000000..c380adb311 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/connect-inject-clusterrolebinding.yaml @@ -0,0 +1,20 @@ +{{- if or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "consul.fullname" . }}-connect-injector + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: connect-injector +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "consul.fullname" . }}-connect-injector +subjects: +- kind: ServiceAccount + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} +{{- end }} \ No newline at end of file diff --git a/charts/hashicorp/consul/1.5.3/templates/connect-inject-deployment.yaml b/charts/hashicorp/consul/1.5.3/templates/connect-inject-deployment.yaml new file mode 100644 index 0000000000..5aaa50a107 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/connect-inject-deployment.yaml @@ -0,0 +1,371 @@ +{{- if and .Values.global.peering.enabled (not .Values.connectInject.enabled) }}{{ fail "setting global.peering.enabled to true requires connectInject.enabled to be true" }}{{ end }} +{{- if and .Values.global.peering.enabled (not .Values.global.tls.enabled) }}{{ fail "setting global.peering.enabled to true requires global.tls.enabled to be true" }}{{ end }} +{{- if and .Values.global.peering.enabled (not .Values.meshGateway.enabled) }}{{ fail "setting global.peering.enabled to true requires meshGateway.enabled to be true" }}{{ end }} +{{- if (or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled)) }} +{{- if and .Values.global.adminPartitions.enabled (not .Values.global.enableConsulNamespaces) }}{{ fail "global.enableConsulNamespaces must be true if global.adminPartitions.enabled=true" }}{{ end }} +{{ template "consul.validateVaultWebhookCertConfiguration" . }} +{{- template "consul.reservedNamesFailer" (list .Values.connectInject.consulNamespaces.consulDestinationNamespace "connectInject.consulNamespaces.consulDestinationNamespace") }} +{{- if and .Values.externalServers.enabled (not .Values.externalServers.hosts) }}{{ fail "externalServers.hosts must be set if externalServers.enabled is true" }}{{ end -}} +{{- if and .Values.externalServers.enabled .Values.global.cloud.enabled }} + {{- if and (gt (len .Values.externalServers.hosts) 0) (regexMatch ".+.hashicorp.cloud$" ( first .Values.externalServers.hosts )) }}{{fail "global.cloud.enabled cannot be used in combination with an HCP-managed cluster address in externalServers.hosts. global.cloud.enabled is for linked self-managed clusters."}}{{- end }} +{{- end }} +{{- if and .Values.externalServers.skipServerWatch (not .Values.externalServers.enabled) }}{{ fail "externalServers.enabled must be set if externalServers.skipServerWatch is true" }}{{ end -}} +{{- $dnsEnabled := (or (and (ne (.Values.dns.enabled | toString) "-") .Values.dns.enabled) (and (eq (.Values.dns.enabled | toString) "-") .Values.connectInject.transparentProxy.defaultEnabled)) -}} +{{- $dnsRedirectionEnabled := (or (and (ne (.Values.dns.enableRedirection | toString) "-") .Values.dns.enableRedirection) (and (eq (.Values.dns.enableRedirection | toString) "-") .Values.connectInject.transparentProxy.defaultEnabled)) -}} +{{ template "consul.validateRequiredCloudSecretsExist" . }} +{{ template "consul.validateCloudSecretKeys" . }} +{{ template "consul.validateResourceAPIs" . }} +# The deployment for running the Connect sidecar injector +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: connect-injector + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} +spec: + replicas: {{ .Values.connectInject.replicas }} + selector: + matchLabels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: connect-injector + template: + metadata: + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: connect-injector + {{- if .Values.connectInject.extraLabels }} + {{- toYaml .Values.connectInject.extraLabels | nindent 8 }} + {{- end }} + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} + annotations: + "consul.hashicorp.com/connect-inject": "false" + "consul.hashicorp.com/mesh-inject": "false" + {{- if .Values.connectInject.annotations }} + {{- tpl .Values.connectInject.annotations . | nindent 8 }} + {{- end }} + {{- if (and .Values.global.secretsBackend.vault.enabled .Values.global.tls.enabled) }} + "vault.hashicorp.com/agent-init-first": "true" + "vault.hashicorp.com/agent-inject": "true" + {{- if .Values.global.secretsBackend.vault.connectInjectRole }} + "vault.hashicorp.com/role": {{ .Values.global.secretsBackend.vault.connectInjectRole }} + {{ else }} + "vault.hashicorp.com/role": {{ .Values.global.secretsBackend.vault.consulCARole }} + {{ end }} + "vault.hashicorp.com/agent-inject-secret-serverca.crt": {{ .Values.global.tls.caCert.secretName }} + "vault.hashicorp.com/agent-inject-template-serverca.crt": {{ template "consul.serverTLSCATemplate" . }} + {{- if .Values.global.secretsBackend.vault.connectInject.caCert.secretName }} + {{- with .Values.global.secretsBackend.vault.connectInject.caCert }} + "vault.hashicorp.com/agent-inject-secret-ca.crt": {{ .secretName }} + "vault.hashicorp.com/agent-inject-template-ca.crt": {{ template "consul.vaultCATemplate" . }} + "vault.hashicorp.com/secret-volume-path-ca.crt": "/vault/secrets/connect-injector/certs" + {{- end }} + {{- end }} + {{- if .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName }} + "vault.hashicorp.com/agent-inject-secret-tls.crt": {{ .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName }} + "vault.hashicorp.com/agent-inject-template-tls.crt": {{ include "consul.connectInjectWebhookTLSCertTemplate" . }} + "vault.hashicorp.com/secret-volume-path-tls.crt": "/vault/secrets/connect-injector/certs" + "vault.hashicorp.com/agent-inject-secret-tls.key": {{ .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName }} + "vault.hashicorp.com/agent-inject-template-tls.key": {{ include "consul.connectInjectWebhookTLSKeyTemplate" . }} + "vault.hashicorp.com/secret-volume-path-tls.key": "/vault/secrets/connect-injector/certs" + {{- end }} + {{- if and .Values.global.secretsBackend.vault.ca.secretName .Values.global.secretsBackend.vault.ca.secretKey }} + "vault.hashicorp.com/agent-extra-secret": "{{ .Values.global.secretsBackend.vault.ca.secretName }}" + "vault.hashicorp.com/ca-cert": "/vault/custom/{{ .Values.global.secretsBackend.vault.ca.secretKey }}" + {{- end }} + {{- if .Values.global.secretsBackend.vault.agentAnnotations }} + {{ tpl .Values.global.secretsBackend.vault.agentAnnotations . | nindent 8 | trim }} + {{- end }} + {{- if (and (.Values.global.secretsBackend.vault.vaultNamespace) (not (hasKey (default "" .Values.global.secretsBackend.vault.agentAnnotations | fromYaml) "vault.hashicorp.com/namespace")))}} + "vault.hashicorp.com/namespace": "{{ .Values.global.secretsBackend.vault.vaultNamespace }}" + {{- end }} + {{- end }} + spec: + serviceAccountName: {{ template "consul.fullname" . }}-connect-injector + containers: + - name: sidecar-injector + image: "{{ default .Values.global.imageK8S .Values.connectInject.image }}" + {{ template "consul.imagePullPolicy" . }} + ports: + - containerPort: 8080 + name: webhook-server + protocol: TCP + {{- include "consul.restrictedSecurityContext" . | nindent 10 }} + env: + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + {{- include "consul.consulK8sConsulServerEnvVars" . | nindent 12 }} + {{- if .Values.global.acls.manageSystemACLs }} + - name: CONSUL_LOGIN_AUTH_METHOD + {{- if and .Values.global.federation.enabled .Values.global.federation.primaryDatacenter }} + value: {{ template "consul.fullname" . }}-k8s-component-auth-method-{{ .Values.global.datacenter }} + {{- else }} + value: {{ template "consul.fullname" . }}-k8s-component-auth-method + {{- end }} + - name: CONSUL_LOGIN_DATACENTER + {{- if and .Values.global.federation.enabled .Values.global.federation.primaryDatacenter }} + value: {{ .Values.global.federation.primaryDatacenter }} + {{- else }} + value: {{ .Values.global.datacenter }} + {{- end }} + - name: CONSUL_LOGIN_META + value: "component=connect-injector,pod=$(NAMESPACE)/$(POD_NAME)" + {{- end }} + {{- if (and .Values.connectInject.aclInjectToken.secretName .Values.connectInject.aclInjectToken.secretKey) }} + - name: CONSUL_ACL_TOKEN + valueFrom: + secretKeyRef: + name: {{ .Values.connectInject.aclInjectToken.secretName }} + key: {{ .Values.connectInject.aclInjectToken.secretKey }} + {{- end }} + command: + - "/bin/sh" + - "-ec" + - | + exec consul-k8s-control-plane inject-connect \ + {{- if .Values.global.federation.enabled }} + -enable-federation \ + {{- end }} + -log-level={{ default .Values.global.logLevel .Values.connectInject.logLevel }} \ + -log-json={{ .Values.global.logJSON }} \ + -default-inject={{ .Values.connectInject.default }} \ + -consul-image="{{ default .Values.global.image .Values.connectInject.imageConsul }}" \ + -consul-dataplane-image="{{ .Values.global.imageConsulDataplane }}" \ + -consul-k8s-image="{{ default .Values.global.imageK8S .Values.connectInject.image }}" \ + -release-name="{{ .Release.Name }}" \ + -release-namespace="{{ .Release.Namespace }}" \ + -resource-prefix={{ template "consul.fullname" . }} \ + -listen=:8080 \ + {{- if (mustHas "resource-apis" .Values.global.experiments) }} + -enable-resource-apis=true \ + {{- end }} + {{- if (mustHas "v2tenancy" .Values.global.experiments) }} + -enable-v2tenancy=true \ + {{- end }} + {{- range $k, $v := .Values.connectInject.consulNode.meta }} + -node-meta={{ $k }}={{ $v }} \ + {{- end }} + {{- if .Values.connectInject.transparentProxy.defaultEnabled }} + -default-enable-transparent-proxy=true \ + {{- else }} + -default-enable-transparent-proxy=false \ + {{- end }} + -enable-cni={{ .Values.connectInject.cni.enabled }} \ + {{- if .Values.global.peering.enabled }} + -enable-peering=true \ + {{- end }} + {{- if .Values.global.openshift.enabled }} + -enable-openshift \ + {{- end }} + {{- if .Values.connectInject.transparentProxy.defaultOverwriteProbes }} + -transparent-proxy-default-overwrite-probes=true \ + {{- else }} + -transparent-proxy-default-overwrite-probes=false \ + {{- end }} + {{- if (and $dnsEnabled $dnsRedirectionEnabled) }} + -enable-consul-dns=true \ + {{- end }} + {{- if .Values.global.openshift.enabled }} + -enable-openshift \ + {{- end }} + {{- if (or (and (ne (.Values.connectInject.metrics.defaultEnabled | toString) "-") .Values.connectInject.metrics.defaultEnabled) (and (eq (.Values.connectInject.metrics.defaultEnabled | toString) "-") .Values.global.metrics.enabled)) }} + -default-enable-metrics=true \ + {{- else }} + -default-enable-metrics=false \ + {{- end }} + -enable-gateway-metrics={{ .Values.global.metrics.enableGatewayMetrics }} \ + -default-enable-metrics-merging={{ .Values.connectInject.metrics.defaultEnableMerging }} \ + -default-merged-metrics-port={{ .Values.connectInject.metrics.defaultMergedMetricsPort }} \ + -default-prometheus-scrape-port={{ .Values.connectInject.metrics.defaultPrometheusScrapePort }} \ + -default-prometheus-scrape-path="{{ .Values.connectInject.metrics.defaultPrometheusScrapePath }}" \ + {{- if .Values.connectInject.envoyExtraArgs }} + -envoy-extra-args="{{ .Values.connectInject.envoyExtraArgs }}" \ + {{- end }} + {{- if .Values.connectInject.overrideAuthMethodName }} + -acl-auth-method="{{ .Values.connectInject.overrideAuthMethodName }}" \ + {{- else if .Values.global.acls.manageSystemACLs }} + -acl-auth-method="{{ template "consul.fullname" . }}-k8s-auth-method" \ + {{- end }} + {{- range $value := .Values.connectInject.k8sAllowNamespaces }} + -allow-k8s-namespace="{{ $value }}" \ + {{- end }} + {{- range $value := .Values.connectInject.k8sDenyNamespaces }} + -deny-k8s-namespace="{{ $value }}" \ + {{- end }} + {{- if .Values.global.adminPartitions.enabled }} + -enable-partitions=true \ + {{- end }} + {{- if .Values.global.enableConsulNamespaces }} + -enable-namespaces=true \ + {{- if .Values.connectInject.consulNamespaces.consulDestinationNamespace }} + -consul-destination-namespace={{ .Values.connectInject.consulNamespaces.consulDestinationNamespace }} \ + {{- end }} + {{- if and .Values.global.enableConsulNamespaces .Values.connectInject.consulNamespaces.mirroringK8S }} + -enable-k8s-namespace-mirroring=true \ + {{- if .Values.connectInject.consulNamespaces.mirroringK8SPrefix }} + -k8s-namespace-mirroring-prefix={{ .Values.connectInject.consulNamespaces.mirroringK8SPrefix }} \ + {{- end }} + {{- end }} + {{- if .Values.global.acls.manageSystemACLs }} + -consul-cross-namespace-acl-policy=cross-namespace-policy \ + {{- end }} + {{- end }} + {{- if and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName }} + -tls-cert-dir=/vault/secrets/connect-injector/certs \ + -enable-webhook-ca-update \ + {{- else }} + -tls-cert-dir=/etc/connect-injector/certs \ + {{- end }} + {{- $resources := .Values.connectInject.sidecarProxy.resources }} + {{- /* kindIs is used here to differentiate between null and 0 */}} + {{- if not (kindIs "invalid" $resources.limits.memory) }} + -default-sidecar-proxy-memory-limit={{ $resources.limits.memory }} \ + {{- end }} + {{- if not (kindIs "invalid" $resources.requests.memory) }} + -default-sidecar-proxy-memory-request={{ $resources.requests.memory }} \ + {{- end }} + {{- if not (kindIs "invalid" $resources.limits.cpu) }} + -default-sidecar-proxy-cpu-limit={{ $resources.limits.cpu }} \ + {{- end }} + {{- if not (kindIs "invalid" $resources.requests.cpu) }} + -default-sidecar-proxy-cpu-request={{ $resources.requests.cpu }} \ + {{- end }} + -default-envoy-proxy-concurrency={{ .Values.connectInject.sidecarProxy.concurrency }} \ + {{- if .Values.connectInject.sidecarProxy.lifecycle.defaultEnabled }} + -default-enable-sidecar-proxy-lifecycle=true \ + {{- else }} + -default-enable-sidecar-proxy-lifecycle=false \ + {{- end }} + {{- if .Values.connectInject.sidecarProxy.lifecycle.defaultEnableShutdownDrainListeners }} + -default-enable-sidecar-proxy-lifecycle-shutdown-drain-listeners=true \ + {{- else }} + -default-enable-sidecar-proxy-lifecycle-shutdown-drain-listeners=false \ + {{- end }} + -default-sidecar-proxy-lifecycle-shutdown-grace-period-seconds={{ .Values.connectInject.sidecarProxy.lifecycle.defaultShutdownGracePeriodSeconds }} \ + -default-sidecar-proxy-lifecycle-startup-grace-period-seconds={{ .Values.connectInject.sidecarProxy.lifecycle.defaultStartupGracePeriodSeconds }} \ + -default-sidecar-proxy-lifecycle-graceful-port={{ .Values.connectInject.sidecarProxy.lifecycle.defaultGracefulPort }} \ + -default-sidecar-proxy-lifecycle-graceful-shutdown-path="{{ .Values.connectInject.sidecarProxy.lifecycle.defaultGracefulShutdownPath }}" \ + -default-sidecar-proxy-lifecycle-graceful-startup-path="{{ .Values.connectInject.sidecarProxy.lifecycle.defaultGracefulStartupPath }}" \ + -default-sidecar-proxy-startup-failure-seconds={{ .Values.connectInject.sidecarProxy.defaultStartupFailureSeconds }} \ + -default-sidecar-proxy-liveness-failure-seconds={{ .Values.connectInject.sidecarProxy.defaultLivenessFailureSeconds }} \ + {{- if .Values.connectInject.initContainer }} + {{- $initResources := .Values.connectInject.initContainer.resources }} + {{- if not (kindIs "invalid" $initResources.limits.memory) }} + -init-container-memory-limit={{ $initResources.limits.memory }} \ + {{- end }} + {{- if not (kindIs "invalid" $initResources.requests.memory) }} + -init-container-memory-request={{ $initResources.requests.memory }} \ + {{- end }} + {{- if not (kindIs "invalid" $initResources.limits.cpu) }} + -init-container-cpu-limit={{ $initResources.limits.cpu }} \ + {{- end }} + {{- if not (kindIs "invalid" $initResources.requests.cpu) }} + -init-container-cpu-request={{ $initResources.requests.cpu }} \ + {{- end }} + {{- end }} + + {{- if .Values.global.cloud.enabled }} + -tls-server-name=server.{{ .Values.global.datacenter}}.{{ .Values.global.domain}} \ + {{- end }} + {{- if and .Values.global.tls.enabled .Values.global.tls.enableAutoEncrypt }} + -enable-auto-encrypt \ + {{- end }} + -enable-telemetry-collector={{ .Values.global.metrics.enableTelemetryCollector}} \ + startupProbe: + httpGet: + path: /readyz/ready + port: 9445 + scheme: HTTP + initialDelaySeconds: 30 + failureThreshold: 15 + periodSeconds: 2 + timeoutSeconds: 5 + livenessProbe: + httpGet: + path: /readyz/ready + port: 9445 + scheme: HTTP + failureThreshold: 2 + initialDelaySeconds: 1 + successThreshold: 1 + timeoutSeconds: 5 + readinessProbe: + httpGet: + path: /readyz/ready + port: 9445 + scheme: HTTP + failureThreshold: 2 + initialDelaySeconds: 2 + successThreshold: 1 + timeoutSeconds: 5 + volumeMounts: + {{- if not (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName) }} + - name: certs + mountPath: /etc/connect-injector/certs + readOnly: true + {{- end }} + {{- if and .Values.global.tls.enabled (not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled))}} + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- with .Values.connectInject.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumes: + {{- if not (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName) }} + - name: certs + secret: + defaultMode: 420 + secretName: {{ template "consul.fullname" . }}-connect-inject-webhook-cert + {{- end }} + {{- if .Values.global.tls.enabled }} + {{- if not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled) }} + - name: consul-ca-cert + secret: + {{- if .Values.global.tls.caCert.secretName }} + secretName: {{ .Values.global.tls.caCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" . }}-ca-cert + {{- end }} + items: + - key: {{ default "tls.crt" .Values.global.tls.caCert.secretKey }} + path: tls.crt + {{- end }} + {{- end }} + {{- if .Values.connectInject.priorityClassName }} + priorityClassName: {{ .Values.connectInject.priorityClassName | quote }} + {{- end }} + {{- if .Values.connectInject.nodeSelector }} + nodeSelector: + {{ tpl .Values.connectInject.nodeSelector . | indent 8 | trim }} + {{- end }} + {{- if .Values.connectInject.affinity }} + affinity: + {{ tpl .Values.connectInject.affinity . | indent 8 | trim }} + {{- end }} + {{- if .Values.connectInject.tolerations }} + tolerations: + {{ tpl .Values.connectInject.tolerations . | indent 8 | trim }} + {{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/connect-inject-leader-election-role.yaml b/charts/hashicorp/consul/1.5.3/templates/connect-inject-leader-election-role.yaml new file mode 100644 index 0000000000..703aaffaac --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/connect-inject-leader-election-role.yaml @@ -0,0 +1,41 @@ +{{- if or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" . }}-connect-inject-leader-election + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: connect-injector +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/connect-inject-leader-election-rolebinding.yaml b/charts/hashicorp/consul/1.5.3/templates/connect-inject-leader-election-rolebinding.yaml new file mode 100644 index 0000000000..9a27d3c868 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/connect-inject-leader-election-rolebinding.yaml @@ -0,0 +1,21 @@ +{{- if or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" . }}-connect-inject-leader-election + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: connect-injector +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" . }}-connect-inject-leader-election +subjects: +- kind: ServiceAccount + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/connect-inject-mutatingwebhookconfiguration.yaml b/charts/hashicorp/consul/1.5.3/templates/connect-inject-mutatingwebhookconfiguration.yaml new file mode 100644 index 0000000000..e65c386636 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/connect-inject-mutatingwebhookconfiguration.yaml @@ -0,0 +1,381 @@ +{{- if (or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled)) }} +# The MutatingWebhookConfiguration to enable the Connect injector. +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: connect-injector +webhooks: +- clientConfig: + service: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + path: /mutate-v1alpha1-proxydefaults + failurePolicy: Fail + admissionReviewVersions: + - "v1beta1" + - "v1" + name: mutate-proxydefaults.consul.hashicorp.com + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - proxydefaults + sideEffects: None +- clientConfig: + service: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + path: /mutate-v1alpha1-mesh + failurePolicy: Fail + admissionReviewVersions: + - "v1beta1" + - "v1" + name: mutate-mesh.consul.hashicorp.com + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - meshes + sideEffects: None +- clientConfig: + service: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + path: /mutate-v1alpha1-servicedefaults + failurePolicy: Fail + admissionReviewVersions: + - "v1beta1" + - "v1" + name: mutate-servicedefaults.consul.hashicorp.com + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - servicedefaults + sideEffects: None +- clientConfig: + service: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + path: /mutate-v1alpha1-serviceresolver + failurePolicy: Fail + admissionReviewVersions: + - "v1beta1" + - "v1" + name: mutate-serviceresolver.consul.hashicorp.com + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - serviceresolvers + sideEffects: None +- clientConfig: + service: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + path: /mutate-v1alpha1-servicerouter + failurePolicy: Fail + admissionReviewVersions: + - "v1beta1" + - "v1" + name: mutate-servicerouter.consul.hashicorp.com + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - servicerouters + sideEffects: None +- clientConfig: + service: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + path: /mutate-v1alpha1-servicesplitter + failurePolicy: Fail + admissionReviewVersions: + - "v1beta1" + - "v1" + name: mutate-servicesplitter.consul.hashicorp.com + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - servicesplitters + sideEffects: None +- clientConfig: + service: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + path: /mutate-v1alpha1-serviceintentions + failurePolicy: Fail + admissionReviewVersions: + - "v1beta1" + - "v1" + name: mutate-serviceintentions.consul.hashicorp.com + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - serviceintentions + sideEffects: None +- clientConfig: + service: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + path: /mutate-v1alpha1-ingressgateway + failurePolicy: Fail + admissionReviewVersions: + - "v1beta1" + - "v1" + name: mutate-ingressgateway.consul.hashicorp.com + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - ingressgateways + sideEffects: None +- clientConfig: + service: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + path: /mutate-v1alpha1-terminatinggateway + failurePolicy: Fail + admissionReviewVersions: + - "v1beta1" + - "v1" + name: mutate-terminatinggateway.consul.hashicorp.com + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - terminatinggateways + sideEffects: None +- clientConfig: + service: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + path: /mutate-v1alpha1-exportedservices + failurePolicy: Fail + admissionReviewVersions: + - "v1beta1" + - "v1" + name: mutate-exportedservices.consul.hashicorp.com + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - exportedservices + sideEffects: None +- clientConfig: + service: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + path: /mutate-v1alpha1-controlplanerequestlimits + failurePolicy: Fail + admissionReviewVersions: + - "v1beta1" + - "v1" + name: mutate-controlplanerequestlimit.consul.hashicorp.com + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - controlplanerequestlimits + sideEffects: None +- name: {{ template "consul.fullname" . }}-connect-injector.consul.hashicorp.com + # The webhook will fail scheduling all pods that are not part of consul if all replicas of the webhook are unhealthy. + objectSelector: + matchExpressions: + - key: app + operator: NotIn + values: [ {{ template "consul.name" . }} ] + failurePolicy: {{ .Values.connectInject.failurePolicy }} + sideEffects: None + admissionReviewVersions: + - "v1beta1" + - "v1" + clientConfig: + service: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + path: "/mutate" + rules: + - operations: [ "CREATE" ] + apiGroups: [ "" ] + apiVersions: [ "v1" ] + resources: [ "pods" ] +{{- if .Values.connectInject.namespaceSelector }} + namespaceSelector: +{{ tpl .Values.connectInject.namespaceSelector . | indent 6 }} +{{- end }} +{{- if .Values.global.peering.enabled }} +- name: {{ template "consul.fullname" . }}-mutate-peeringacceptors.consul.hashicorp.com + clientConfig: + service: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + path: "/mutate-v1alpha1-peeringacceptors" + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - peeringacceptors + failurePolicy: Fail + sideEffects: None + admissionReviewVersions: + - "v1beta1" + - "v1" +- name: {{ template "consul.fullname" . }}-mutate-peeringdialers.consul.hashicorp.com + clientConfig: + service: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + path: "/mutate-v1alpha1-peeringdialers" + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - peeringdialers + failurePolicy: Fail + sideEffects: None + admissionReviewVersions: + - "v1beta1" + - "v1" +- admissionReviewVersions: + - v1beta1 + - v1 + clientConfig: + service: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + path: /mutate-v1alpha1-samenessgroup + failurePolicy: Fail + name: mutate-samenessgroup.consul.hashicorp.com + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - samenessgroups + sideEffects: None +{{- if (mustHas "resource-apis" .Values.global.experiments) }} +- admissionReviewVersions: + - v1beta1 + - v1 + clientConfig: + service: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + path: /mutate-v2beta1-trafficpermissions + failurePolicy: Fail + name: mutate-trafficpermissions.auth.consul.hashicorp.com + rules: + - apiGroups: + - auth.consul.hashicorp.com + apiVersions: + - v2beta1 + operations: + - CREATE + - UPDATE + resources: + - trafficpermissions + sideEffects: None +{{- end }} +{{- end }} +- admissionReviewVersions: + - v1beta1 + - v1 + clientConfig: + service: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + path: /mutate-v1alpha1-jwtprovider + failurePolicy: Fail + name: mutate-jwtprovider.consul.hashicorp.com + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - jwtproviders + sideEffects: None +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/connect-inject-podsecuritypolicy.yaml b/charts/hashicorp/consul/1.5.3/templates/connect-inject-podsecuritypolicy.yaml new file mode 100644 index 0000000000..0fafef7c40 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/connect-inject-podsecuritypolicy.yaml @@ -0,0 +1,40 @@ +{{- if (and .Values.global.enablePodSecurityPolicies (or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled))) }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: connect-injector +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + # Allow core volume types. + volumes: + - 'configMap' + - 'emptyDir' + - 'projected' + - 'secret' + - 'downwardAPI' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/connect-inject-service.yaml b/charts/hashicorp/consul/1.5.3/templates/connect-inject-service.yaml new file mode 100644 index 0000000000..b0284af74d --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/connect-inject-service.yaml @@ -0,0 +1,23 @@ +{{- if (or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled)) }} +# The service for the Connect sidecar injector +apiVersion: v1 +kind: Service +metadata: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: connect-injector +spec: + ports: + - port: 443 + targetPort: 8080 + selector: + app: {{ template "consul.name" . }} + release: "{{ .Release.Name }}" + component: connect-injector +{{- end }} + diff --git a/charts/hashicorp/consul/1.5.3/templates/connect-inject-serviceaccount.yaml b/charts/hashicorp/consul/1.5.3/templates/connect-inject-serviceaccount.yaml new file mode 100644 index 0000000000..ea2352c7ac --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/connect-inject-serviceaccount.yaml @@ -0,0 +1,23 @@ +{{- if or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled) }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: connect-injector + {{- if .Values.connectInject.serviceAccount.annotations }} + annotations: + {{ tpl .Values.connectInject.serviceAccount.annotations . | nindent 4 | trim }} + {{- end }} +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} +- name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/connect-inject-validatingwebhookconfiguration.yaml b/charts/hashicorp/consul/1.5.3/templates/connect-inject-validatingwebhookconfiguration.yaml new file mode 100644 index 0000000000..92068bbf68 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/connect-inject-validatingwebhookconfiguration.yaml @@ -0,0 +1,47 @@ +{{- if (or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled)) }} +# The ValidatingWebhookConfiguration to enable the Connect injector. +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: connect-injector +webhooks: +- name: validate-gatewaypolicy.consul.hashicorp.com + matchPolicy: Equivalent + rules: + - operations: [ "CREATE" , "UPDATE" ] + apiGroups: [ "consul.hashicorp.com" ] + apiVersions: [ "v1alpha1" ] + resources: [ "gatewaypolicies" ] + failurePolicy: Fail + sideEffects: None + admissionReviewVersions: + - v1 + clientConfig: + service: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + path: /validate-v1alpha1-gatewaypolicy +- name: validate-registration.consul.hashicorp.com + matchPolicy: Equivalent + rules: + - operations: [ "CREATE" , "UPDATE" ] + apiGroups: [ "consul.hashicorp.com" ] + apiVersions: [ "v1alpha1" ] + resources: [ "registrations" ] + failurePolicy: Fail + sideEffects: None + admissionReviewVersions: + - v1 + clientConfig: + service: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + path: /validate-v1alpha1-registration +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/connect-injector-disruptionbudget.yaml b/charts/hashicorp/consul/1.5.3/templates/connect-injector-disruptionbudget.yaml new file mode 100644 index 0000000000..9b9cf2e39e --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/connect-injector-disruptionbudget.yaml @@ -0,0 +1,30 @@ +{{- if (and .Values.connectInject.disruptionBudget.enabled (or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled))) }} +# PodDisruptionBudget to prevent degrading the connectInject cluster through +# voluntary cluster changes. +{{- if .Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" }} +apiVersion: policy/v1 +{{- else }} +apiVersion: policy/v1beta1 +{{- end }} +kind: PodDisruptionBudget +metadata: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: connect-injector +spec: + {{- if .Values.connectInject.disruptionBudget.minAvailable }} + minAvailable: {{ .Values.connectInject.disruptionBudget.minAvailable }} + {{- else }} + maxUnavailable: {{ template "consul.pdb.connectInject.maxUnavailable" . }} + {{- end }} + selector: + matchLabels: + app: {{ template "consul.name" . }} + release: "{{ .Release.Name }}" + component: connect-injector +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-apigateways.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-apigateways.yaml new file mode 100644 index 0000000000..c790c6ddf9 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-apigateways.yaml @@ -0,0 +1,317 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: apigateways.mesh.consul.hashicorp.com +spec: + group: mesh.consul.hashicorp.com + names: + kind: APIGateway + listKind: APIGatewayList + plural: apigateways + singular: apigateway + scope: Cluster + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v2beta1 + schema: + openAPIV3Schema: + description: APIGateway is the Schema for the API Gateway + 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: + properties: + gatewayClassName: + description: GatewayClassName is the name of the GatewayClass used + by the APIGateway + type: string + listeners: + items: + properties: + hostname: + description: |- + Hostname is the host name that a listener should be bound to, if + unspecified, the listener accepts requests for all hostnames. + type: string + name: + description: |- + Name is the name of the listener in a given gateway. This must be + unique within a gateway. + type: string + port: + format: int32 + maximum: 65535 + minimum: 0 + type: integer + protocol: + description: |- + Protocol is the protocol that a listener should use, it must + either be "http" or "tcp" + type: string + tls: + description: TLS is the TLS settings for the listener. + properties: + certificates: + description: |- + Certificates is a set of references to certificates + that a gateway listener uses for TLS termination. + items: + description: |- + Reference identifies which resource a condition relates to, when it is not + the core resource itself. + properties: + name: + description: Name is the user-given name of the resource + (e.g. the "billing" service). + type: string + section: + description: Section identifies which part of the + resource the condition relates to. + type: string + tenancy: + description: |- + Tenancy identifies the tenancy units (i.e. partition, namespace) in which + the resource resides. + properties: + namespace: + description: |- + Namespace further isolates resources within a partition. + https://developer.hashicorp.com/consul/docs/enterprise/namespaces + + + When using the List and WatchList endpoints, provide the wildcard value "*" + to list resources across all namespaces. + type: string + partition: + description: |- + Partition is the topmost administrative boundary within a cluster. + https://developer.hashicorp.com/consul/docs/enterprise/admin-partitions + + + When using the List and WatchList endpoints, provide the wildcard value "*" + to list resources across all partitions. + type: string + type: object + type: + description: Type identifies the resource's type. + properties: + group: + description: |- + Group describes the area of functionality to which this resource type + relates (e.g. "catalog", "authorization"). + type: string + groupVersion: + description: |- + GroupVersion is incremented when sweeping or backward-incompatible changes + are made to the group's resource types. + type: string + kind: + description: Kind identifies the specific resource + type within the group. + type: string + type: object + type: object + type: array + tlsParameters: + description: TLSParameters contains optional configuration + for running TLS termination. + properties: + cipherSuites: + items: + enum: + - TLS_CIPHER_SUITE_ECDHE_ECDSA_AES128_GCM_SHA256 + - TLS_CIPHER_SUITE_AES256_SHA + - TLS_CIPHER_SUITE_ECDHE_ECDSA_CHACHA20_POLY1305 + - TLS_CIPHER_SUITE_ECDHE_RSA_AES128_GCM_SHA256 + - TLS_CIPHER_SUITE_ECDHE_RSA_CHACHA20_POLY1305 + - TLS_CIPHER_SUITE_ECDHE_ECDSA_AES128_SHA + - TLS_CIPHER_SUITE_ECDHE_RSA_AES128_SHA + - TLS_CIPHER_SUITE_AES128_GCM_SHA256 + - TLS_CIPHER_SUITE_AES128_SHA + - TLS_CIPHER_SUITE_ECDHE_ECDSA_AES256_GCM_SHA384 + - TLS_CIPHER_SUITE_ECDHE_RSA_AES256_GCM_SHA384 + - TLS_CIPHER_SUITE_ECDHE_ECDSA_AES256_SHA + - TLS_CIPHER_SUITE_ECDHE_RSA_AES256_SHA + - TLS_CIPHER_SUITE_AES256_GCM_SHA384 + format: int32 + type: string + type: array + maxVersion: + enum: + - TLS_VERSION_AUTO + - TLS_VERSION_1_0 + - TLS_VERSION_1_1 + - TLS_VERSION_1_2 + - TLS_VERSION_1_3 + - TLS_VERSION_INVALID + - TLS_VERSION_UNSPECIFIED + format: int32 + type: string + minVersion: + enum: + - TLS_VERSION_AUTO + - TLS_VERSION_1_0 + - TLS_VERSION_1_1 + - TLS_VERSION_1_2 + - TLS_VERSION_1_3 + - TLS_VERSION_INVALID + - TLS_VERSION_UNSPECIFIED + format: int32 + type: string + type: object + type: object + type: object + minItems: 1 + type: array + type: object + status: + properties: + addresses: + items: + properties: + type: + default: IPAddress + type: string + value: + type: string + required: + - type + - value + type: object + type: array + listeners: + items: + properties: + attachedRoutes: + format: int32 + type: integer + name: + type: string + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: |- + Conditions define a readiness condition for a Consul resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the + condition transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details + about the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, + False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource + successfully synced with Consul. + format: date-time + type: string + type: object + required: + - attachedRoutes + - name + type: object + type: array + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: |- + Conditions define a readiness condition for a Consul resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details + about the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, + Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-controlplanerequestlimits.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-controlplanerequestlimits.yaml new file mode 100644 index 0000000000..4e11ceb1c3 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-controlplanerequestlimits.yaml @@ -0,0 +1,201 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: controlplanerequestlimits.consul.hashicorp.com +spec: + group: consul.hashicorp.com + names: + kind: ControlPlaneRequestLimit + listKind: ControlPlaneRequestLimitList + plural: controlplanerequestlimits + singular: controlplanerequestlimit + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: ControlPlaneRequestLimit is the Schema for the controlplanerequestlimits + 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: ControlPlaneRequestLimitSpec defines the desired state of + ControlPlaneRequestLimit. + properties: + acl: + properties: + readRate: + type: number + writeRate: + type: number + type: object + catalog: + properties: + readRate: + type: number + writeRate: + type: number + type: object + configEntry: + properties: + readRate: + type: number + writeRate: + type: number + type: object + connectCA: + properties: + readRate: + type: number + writeRate: + type: number + type: object + coordinate: + properties: + readRate: + type: number + writeRate: + type: number + type: object + discoveryChain: + properties: + readRate: + type: number + writeRate: + type: number + type: object + health: + properties: + readRate: + type: number + writeRate: + type: number + type: object + intention: + properties: + readRate: + type: number + writeRate: + type: number + type: object + kv: + properties: + readRate: + type: number + writeRate: + type: number + type: object + mode: + type: string + preparedQuery: + properties: + readRate: + type: number + writeRate: + type: number + type: object + readRate: + type: number + session: + properties: + readRate: + type: number + writeRate: + type: number + type: object + tenancy: + properties: + readRate: + type: number + writeRate: + type: number + type: object + txn: + properties: + readRate: + type: number + writeRate: + type: number + type: object + writeRate: + type: number + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: |- + Conditions define a readiness condition for a Consul resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-exportedservices-v1.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-exportedservices-v1.yaml new file mode 100644 index 0000000000..a7fbd87e27 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-exportedservices-v1.yaml @@ -0,0 +1,147 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: exportedservices.consul.hashicorp.com +spec: + group: consul.hashicorp.com + names: + kind: ExportedServices + listKind: ExportedServicesList + plural: exportedservices + shortNames: + - exported-services + singular: exportedservices + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: ExportedServices is the Schema for the exportedservices 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: ExportedServicesSpec defines the desired state of ExportedServices. + properties: + services: + description: |- + Services is a list of services to be exported and the list of partitions + to expose them to. + items: + description: |- + ExportedService manages the exporting of a service in the local partition to + other partitions. + properties: + consumers: + description: Consumers is a list of downstream consumers of + the service to be exported. + items: + description: ServiceConsumer represents a downstream consumer + of the service to be exported. + properties: + partition: + description: Partition is the admin partition to export + the service to. + type: string + peer: + description: Peer is the name of the peer to export the + service to. + type: string + samenessGroup: + description: SamenessGroup is the name of the sameness + group to export the service to. + type: string + type: object + type: array + name: + description: Name is the name of the service to be exported. + type: string + namespace: + description: Namespace is the namespace to export the service + from. + type: string + type: object + type: array + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: |- + Conditions define a readiness condition for a Consul resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-exportedservices.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-exportedservices.yaml new file mode 100644 index 0000000000..4b9beb651d --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-exportedservices.yaml @@ -0,0 +1,114 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: exportedservices.multicluster.consul.hashicorp.com +spec: + group: multicluster.consul.hashicorp.com + names: + kind: ExportedServices + listKind: ExportedServicesList + plural: exportedservices + singular: exportedservices + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v2 + schema: + openAPIV3Schema: + description: ExportedServices is the Schema for the Exported Services 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: + properties: + consumers: + items: + type: object + x-kubernetes-preserve-unknown-fields: true + type: array + services: + items: + type: string + type: array + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: |- + Conditions define a readiness condition for a Consul resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-gatewayclassconfigs-v1.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-gatewayclassconfigs-v1.yaml new file mode 100644 index 0000000000..2db954b93d --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-gatewayclassconfigs-v1.yaml @@ -0,0 +1,232 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: gatewayclassconfigs.consul.hashicorp.com +spec: + group: consul.hashicorp.com + names: + kind: GatewayClassConfig + listKind: GatewayClassConfigList + plural: gatewayclassconfigs + singular: gatewayclassconfig + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: GatewayClassConfig defines the values that may be set on a GatewayClass + for Consul API Gateway. + 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 GatewayClassConfig. + properties: + copyAnnotations: + description: Annotation Information to copy to services or deployments + properties: + service: + description: List of annotations to copy to the gateway service. + items: + type: string + type: array + type: object + deployment: + description: Deployment defines the deployment configuration for the + gateway. + properties: + defaultInstances: + default: 1 + description: Number of gateway instances that should be deployed + by default + format: int32 + maximum: 8 + minimum: 1 + type: integer + maxInstances: + default: 8 + description: Max allowed number of gateway instances + format: int32 + maximum: 8 + minimum: 1 + type: integer + minInstances: + default: 1 + description: Minimum allowed number of gateway instances + format: int32 + maximum: 8 + minimum: 1 + type: integer + resources: + description: Resources defines the resource requirements for the + gateway. + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + type: object + mapPrivilegedContainerPorts: + description: The value to add to privileged ports ( ports < 1024) + for gateway containers + format: int32 + type: integer + metrics: + description: Metrics defines how to configure the metrics for a gateway. + properties: + enabled: + description: |- + Enable metrics for this class of gateways. If unspecified, will inherit + behavior from the global Helm configuration. + type: boolean + path: + description: The path used for metrics. + type: string + port: + description: The port used for metrics. + format: int32 + maximum: 65535 + minimum: 1024 + type: integer + type: object + nodeSelector: + additionalProperties: + type: string + description: |- + NodeSelector is a selector which must be true for the pod to fit on a node. + Selector which must match a node's labels for the pod to be scheduled on that node. + More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ + type: object + openshiftSCCName: + description: The name of the OpenShift SecurityContextConstraints + resource for this gateway class to use. + type: string + podSecurityPolicy: + description: The name of an existing Kubernetes PodSecurityPolicy + to bind to the managed ServiceAccount if ACLs are managed. + type: string + serviceType: + description: Service Type string describes ingress methods for a service + enum: + - ClusterIP + - NodePort + - LoadBalancer + type: string + tolerations: + description: |- + Tolerations allow the scheduler to schedule nodes with matching taints. + More Info: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + type: object + type: object + served: true + storage: true +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-gatewayclassconfigs.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-gatewayclassconfigs.yaml new file mode 100644 index 0000000000..50944acae0 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-gatewayclassconfigs.yaml @@ -0,0 +1,1729 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: gatewayclassconfigs.mesh.consul.hashicorp.com +spec: + group: mesh.consul.hashicorp.com + names: + kind: GatewayClassConfig + listKind: GatewayClassConfigList + plural: gatewayclassconfigs + singular: gatewayclassconfig + scope: Cluster + versions: + - additionalPrinterColumns: + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v2beta1 + schema: + openAPIV3Schema: + description: GatewayClassConfig is the Schema for the Mesh Gateway 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: GatewayClassConfigSpec specifies the desired state of the + GatewayClassConfig CRD. + properties: + annotations: + description: Annotations are applied to the created resource + properties: + inheritFromGateway: + description: |- + InheritFromGateway lists the names/keys of annotations or labels to copy from the Gateway resource. + Any name/key included here will override those in Set if specified on the Gateway. + items: + type: string + type: array + set: + additionalProperties: + type: string + description: |- + Set lists the names/keys and values of annotations or labels to set on the resource. + Any name/key included here will be overridden if present in InheritFromGateway and set on the Gateway. + type: object + type: object + deployment: + description: Deployment contains config specific to the Deployment + created from this GatewayClass + properties: + affinity: + description: Affinity specifies the affinity to use on the created + Deployment. + 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 + annotations: + description: Annotations are applied to the created resource + properties: + inheritFromGateway: + description: |- + InheritFromGateway lists the names/keys of annotations or labels to copy from the Gateway resource. + Any name/key included here will override those in Set if specified on the Gateway. + items: + type: string + type: array + set: + additionalProperties: + type: string + description: |- + Set lists the names/keys and values of annotations or labels to set on the resource. + Any name/key included here will be overridden if present in InheritFromGateway and set on the Gateway. + type: object + type: object + container: + description: Container contains config specific to the created + Deployment's container. + properties: + consul: + description: Consul specifies configuration for the consul-dataplane + container + properties: + logging: + description: Logging specifies the logging configuration + for Consul Dataplane + properties: + level: + description: Level sets the logging level for Consul + Dataplane (debug, info, etc.) + type: string + type: object + type: object + hostPort: + description: HostPort specifies a port to be exposed to the + external host network + format: int32 + type: integer + portModifier: + description: |- + PortModifier specifies the value to be added to every port value for listeners on this gateway. + This is generally used to avoid binding to privileged ports in the container. + format: int32 + type: integer + resources: + description: Resources specifies the resource requirements + for the created Deployment's container + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + type: object + dnsPolicy: + description: DNSPolicy specifies the dns policy to use. These + are set on a per pod basis. + enum: + - Default + - ClusterFirst + - ClusterFirstWithHostNet + - None + type: string + hostNetwork: + description: HostNetwork specifies whether the gateway pods should + run on the host network. + type: boolean + initContainer: + description: InitContainer contains config specific to the created + Deployment's init container. + properties: + consul: + description: Consul specifies configuration for the consul-k8s-control-plane + init container + properties: + logging: + description: Logging specifies the logging configuration + for Consul Dataplane + properties: + level: + description: Level sets the logging level for Consul + Dataplane (debug, info, etc.) + type: string + type: object + type: object + resources: + description: Resources specifies the resource requirements + for the created Deployment's init container + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + type: object + labels: + description: Labels are applied to the created resource + properties: + inheritFromGateway: + description: |- + InheritFromGateway lists the names/keys of annotations or labels to copy from the Gateway resource. + Any name/key included here will override those in Set if specified on the Gateway. + items: + type: string + type: array + set: + additionalProperties: + type: string + description: |- + Set lists the names/keys and values of annotations or labels to set on the resource. + Any name/key included here will be overridden if present in InheritFromGateway and set on the Gateway. + type: object + type: object + nodeSelector: + additionalProperties: + type: string + description: |- + NodeSelector is a feature that constrains the scheduling of a pod to nodes that + match specified labels. + By defining NodeSelector in a pod's configuration, you can ensure that the pod is + only scheduled to nodes with the corresponding labels, providing a way to + influence the placement of workloads based on node attributes. + More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ + type: object + priorityClassName: + description: PriorityClassName specifies the priority class name + to use on the created Deployment. + type: string + replicas: + description: Replicas specifies the configuration to control the + number of replicas for the created Deployment. + properties: + default: + description: Default is the number of replicas assigned to + the Deployment when created + format: int32 + type: integer + max: + description: |- + Max is the maximum number of replicas allowed for a gateway with this class. + If the replica count exceeds this value due to manual or automated scaling, + the replica count will be restored to this value. + format: int32 + type: integer + min: + description: |- + Min is the minimum number of replicas allowed for a gateway with this class. + If the replica count drops below this value due to manual or automated scaling, + the replica count will be restored to this value. + format: int32 + type: integer + type: object + securityContext: + description: SecurityContext specifies the security context for + the created Deployment's Pod. + properties: + fsGroup: + description: |- + A special supplemental group that applies to all containers in a pod. + Some volume types allow the Kubelet to change the ownership of that volume + to be owned by the pod: + + + 1. The owning GID will be the FSGroup + 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) + 3. The permission bits are OR'd with rw-rw---- + + + If unset, the Kubelet will not modify the ownership and permissions of any volume. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + fsGroupChangePolicy: + description: |- + fsGroupChangePolicy defines behavior of changing ownership and permission of the volume + before being exposed inside Pod. This field will only apply to + volume types which support fsGroup based ownership(and permissions). + It will have no effect on ephemeral volume types such as: secret, configmaps + and emptydir. + Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. + Note that this field cannot be set when spec.os.name is windows. + type: string + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in SecurityContext. If set in + both SecurityContext and PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + supplementalGroups: + description: |- + A list of groups applied to the first process run in each container, in addition + to the container's primary GID, the fsGroup (if specified), and group memberships + defined in the container image for the uid of the container process. If unspecified, + no additional groups are added to any container. Note that group memberships + defined in the container image for the uid of the container process are still effective, + even if they are not included in this list. + Note that this field cannot be set when spec.os.name is windows. + items: + format: int64 + type: integer + type: array + sysctls: + description: |- + Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported + sysctls (by the container runtime) might fail to launch. + Note that this field cannot be set when spec.os.name is windows. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options within a container's SecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + tolerations: + description: Tolerations specifies the tolerations to use on the + created Deployment. + 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 + topologySpreadConstraints: + description: |- + TopologySpreadConstraints is a feature that controls how pods are spead across your topology. + More info: https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/ + items: + description: TopologySpreadConstraint specifies how to spread + matching pods among the given topology. + properties: + labelSelector: + description: |- + LabelSelector is used to find matching pods. + Pods that match this label selector are counted to determine the number of pods + in their corresponding topology domain. + 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 + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select the pods over which + spreading will be calculated. The keys are used to lookup values from the + incoming pod labels, those key-value labels are ANDed with labelSelector + to select the group of existing pods over which spreading will be calculated + for the incoming pod. The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. + MatchLabelKeys cannot be set when LabelSelector isn't set. + Keys that don't exist in the incoming pod labels will + be ignored. A null or empty list means only match against labelSelector. + + + This is a beta field and requires the MatchLabelKeysInPodTopologySpread feature gate to be enabled (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + description: |- + MaxSkew describes the degree to which pods may be unevenly distributed. + When `whenUnsatisfiable=DoNotSchedule`, it is the maximum permitted difference + between the number of matching pods in the target topology and the global minimum. + The global minimum is the minimum number of matching pods in an eligible domain + or zero if the number of eligible domains is less than MinDomains. + For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same + labelSelector spread as 2/2/1: + In this case, the global minimum is 1. + | zone1 | zone2 | zone3 | + | P P | P P | P | + - if MaxSkew is 1, incoming pod can only be scheduled to zone3 to become 2/2/2; + scheduling it onto zone1(zone2) would make the ActualSkew(3-1) on zone1(zone2) + violate MaxSkew(1). + - if MaxSkew is 2, incoming pod can be scheduled onto any zone. + When `whenUnsatisfiable=ScheduleAnyway`, it is used to give higher precedence + to topologies that satisfy it. + It's a required field. Default value is 1 and 0 is not allowed. + format: int32 + type: integer + minDomains: + description: |- + MinDomains indicates a minimum number of eligible domains. + When the number of eligible domains with matching topology keys is less than minDomains, + Pod Topology Spread treats "global minimum" as 0, and then the calculation of Skew is performed. + And when the number of eligible domains with matching topology keys equals or greater than minDomains, + this value has no effect on scheduling. + As a result, when the number of eligible domains is less than minDomains, + scheduler won't schedule more than maxSkew Pods to those domains. + If value is nil, the constraint behaves as if MinDomains is equal to 1. + Valid values are integers greater than 0. + When value is not nil, WhenUnsatisfiable must be DoNotSchedule. + + + For example, in a 3-zone cluster, MaxSkew is set to 2, MinDomains is set to 5 and pods with the same + labelSelector spread as 2/2/2: + | zone1 | zone2 | zone3 | + | P P | P P | P P | + The number of domains is less than 5(MinDomains), so "global minimum" is treated as 0. + In this situation, new pod with the same labelSelector cannot be scheduled, + because computed skew will be 3(3 - 0) if new Pod is scheduled to any of the three zones, + it will violate MaxSkew. + + + This is a beta field and requires the MinDomainsInPodTopologySpread feature gate to be enabled (enabled by default). + format: int32 + type: integer + nodeAffinityPolicy: + description: |- + NodeAffinityPolicy indicates how we will treat Pod's nodeAffinity/nodeSelector + when calculating pod topology spread skew. Options are: + - Honor: only nodes matching nodeAffinity/nodeSelector are included in the calculations. + - Ignore: nodeAffinity/nodeSelector are ignored. All nodes are included in the calculations. + + + If this value is nil, the behavior is equivalent to the Honor policy. + This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag. + type: string + nodeTaintsPolicy: + description: |- + NodeTaintsPolicy indicates how we will treat node taints when calculating + pod topology spread skew. Options are: + - Honor: nodes without taints, along with tainted nodes for which the incoming pod + has a toleration, are included. + - Ignore: node taints are ignored. All nodes are included. + + + If this value is nil, the behavior is equivalent to the Ignore policy. + This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag. + type: string + topologyKey: + description: |- + TopologyKey is the key of node labels. Nodes that have a label with this key + and identical values are considered to be in the same topology. + We consider each as a "bucket", and try to put balanced number + of pods into each bucket. + We define a domain as a particular instance of a topology. + Also, we define an eligible domain as a domain whose nodes meet the requirements of + nodeAffinityPolicy and nodeTaintsPolicy. + e.g. If TopologyKey is "kubernetes.io/hostname", each Node is a domain of that topology. + And, if TopologyKey is "topology.kubernetes.io/zone", each zone is a domain of that topology. + It's a required field. + type: string + whenUnsatisfiable: + description: |- + WhenUnsatisfiable indicates how to deal with a pod if it doesn't satisfy + the spread constraint. + - DoNotSchedule (default) tells the scheduler not to schedule it. + - ScheduleAnyway tells the scheduler to schedule the pod in any location, + but giving higher precedence to topologies that would help reduce the + skew. + A constraint is considered "Unsatisfiable" for an incoming pod + if and only if every possible node assignment for that pod would violate + "MaxSkew" on some topology. + For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same + labelSelector spread as 3/1/1: + | zone1 | zone2 | zone3 | + | P P P | P | P | + If WhenUnsatisfiable is set to DoNotSchedule, incoming pod can only be scheduled + to zone2(zone3) to become 3/2/1(3/1/2) as ActualSkew(2-1) on zone2(zone3) satisfies + MaxSkew(1). In other words, the cluster can still be imbalanced, but scheduler + won't make it *more* imbalanced. + It's a required field. + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + type: object + labels: + description: Labels are applied to the created resource + properties: + inheritFromGateway: + description: |- + InheritFromGateway lists the names/keys of annotations or labels to copy from the Gateway resource. + Any name/key included here will override those in Set if specified on the Gateway. + items: + type: string + type: array + set: + additionalProperties: + type: string + description: |- + Set lists the names/keys and values of annotations or labels to set on the resource. + Any name/key included here will be overridden if present in InheritFromGateway and set on the Gateway. + type: object + type: object + role: + description: Role contains config specific to the Role created from + this GatewayClass + properties: + annotations: + description: Annotations are applied to the created resource + properties: + inheritFromGateway: + description: |- + InheritFromGateway lists the names/keys of annotations or labels to copy from the Gateway resource. + Any name/key included here will override those in Set if specified on the Gateway. + items: + type: string + type: array + set: + additionalProperties: + type: string + description: |- + Set lists the names/keys and values of annotations or labels to set on the resource. + Any name/key included here will be overridden if present in InheritFromGateway and set on the Gateway. + type: object + type: object + labels: + description: Labels are applied to the created resource + properties: + inheritFromGateway: + description: |- + InheritFromGateway lists the names/keys of annotations or labels to copy from the Gateway resource. + Any name/key included here will override those in Set if specified on the Gateway. + items: + type: string + type: array + set: + additionalProperties: + type: string + description: |- + Set lists the names/keys and values of annotations or labels to set on the resource. + Any name/key included here will be overridden if present in InheritFromGateway and set on the Gateway. + type: object + type: object + type: object + roleBinding: + description: RoleBinding contains config specific to the RoleBinding + created from this GatewayClass + properties: + annotations: + description: Annotations are applied to the created resource + properties: + inheritFromGateway: + description: |- + InheritFromGateway lists the names/keys of annotations or labels to copy from the Gateway resource. + Any name/key included here will override those in Set if specified on the Gateway. + items: + type: string + type: array + set: + additionalProperties: + type: string + description: |- + Set lists the names/keys and values of annotations or labels to set on the resource. + Any name/key included here will be overridden if present in InheritFromGateway and set on the Gateway. + type: object + type: object + labels: + description: Labels are applied to the created resource + properties: + inheritFromGateway: + description: |- + InheritFromGateway lists the names/keys of annotations or labels to copy from the Gateway resource. + Any name/key included here will override those in Set if specified on the Gateway. + items: + type: string + type: array + set: + additionalProperties: + type: string + description: |- + Set lists the names/keys and values of annotations or labels to set on the resource. + Any name/key included here will be overridden if present in InheritFromGateway and set on the Gateway. + type: object + type: object + type: object + service: + description: Service contains config specific to the Service created + from this GatewayClass + properties: + annotations: + description: Annotations are applied to the created resource + properties: + inheritFromGateway: + description: |- + InheritFromGateway lists the names/keys of annotations or labels to copy from the Gateway resource. + Any name/key included here will override those in Set if specified on the Gateway. + items: + type: string + type: array + set: + additionalProperties: + type: string + description: |- + Set lists the names/keys and values of annotations or labels to set on the resource. + Any name/key included here will be overridden if present in InheritFromGateway and set on the Gateway. + type: object + type: object + labels: + description: Labels are applied to the created resource + properties: + inheritFromGateway: + description: |- + InheritFromGateway lists the names/keys of annotations or labels to copy from the Gateway resource. + Any name/key included here will override those in Set if specified on the Gateway. + items: + type: string + type: array + set: + additionalProperties: + type: string + description: |- + Set lists the names/keys and values of annotations or labels to set on the resource. + Any name/key included here will be overridden if present in InheritFromGateway and set on the Gateway. + type: object + type: object + type: + description: Type specifies the type of Service to use (LoadBalancer, + ClusterIP, etc.) + enum: + - ClusterIP + - NodePort + - LoadBalancer + type: string + type: object + serviceAccount: + description: ServiceAccount contains config specific to the corev1.ServiceAccount + created from this GatewayClass + properties: + annotations: + description: Annotations are applied to the created resource + properties: + inheritFromGateway: + description: |- + InheritFromGateway lists the names/keys of annotations or labels to copy from the Gateway resource. + Any name/key included here will override those in Set if specified on the Gateway. + items: + type: string + type: array + set: + additionalProperties: + type: string + description: |- + Set lists the names/keys and values of annotations or labels to set on the resource. + Any name/key included here will be overridden if present in InheritFromGateway and set on the Gateway. + type: object + type: object + labels: + description: Labels are applied to the created resource + properties: + inheritFromGateway: + description: |- + InheritFromGateway lists the names/keys of annotations or labels to copy from the Gateway resource. + Any name/key included here will override those in Set if specified on the Gateway. + items: + type: string + type: array + set: + additionalProperties: + type: string + description: |- + Set lists the names/keys and values of annotations or labels to set on the resource. + Any name/key included here will be overridden if present in InheritFromGateway and set on the Gateway. + type: object + type: object + type: object + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: |- + Conditions define a readiness condition for a Consul resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-gatewayclasses-external.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-gatewayclasses-external.yaml new file mode 100644 index 0000000000..93435b7fce --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-gatewayclasses-external.yaml @@ -0,0 +1,328 @@ +{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 + gateway.networking.k8s.io/bundle-version: v0.6.2 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + 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 + deprecated: true + deprecationWarning: The v1alpha2 version of GatewayClass has been deprecated and will be removed in a future release of the API. Please upgrade to v1beta1. + name: v1alpha2 + schema: + openAPIV3Schema: + description: "GatewayClass describes a class of Gateways available to the user for creating Gateway resources. \n 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. \n Whenever one or more Gateways are using a GatewayClass, implementations MUST 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. \n 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. \n Example: \"example.net/gateway-controller\". \n This field is not mutable and cannot be empty. \n 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 + 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. \n 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. \n If the referent cannot be found, the GatewayClass's \"InvalidParameters\" status condition will be true. \n 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. + 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. \n 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. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \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: {} + - 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. \n 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. \n Whenever one or more Gateways are using a GatewayClass, implementations MUST 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. \n 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. \n Example: \"example.net/gateway-controller\". \n This field is not mutable and cannot be empty. \n 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 + 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. \n 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. \n If the referent cannot be found, the GatewayClass's \"InvalidParameters\" status condition will be true. \n 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. + 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. \n 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. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \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: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-gatewayclasses.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-gatewayclasses.yaml new file mode 100644 index 0000000000..9880d7db0f --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-gatewayclasses.yaml @@ -0,0 +1,130 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: gatewayclasses.mesh.consul.hashicorp.com +spec: + group: mesh.consul.hashicorp.com + names: + kind: GatewayClass + listKind: GatewayClassList + plural: gatewayclasses + singular: gatewayclass + scope: Cluster + versions: + - additionalPrinterColumns: + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v2beta1 + schema: + openAPIV3Schema: + description: GatewayClass is the Schema for the Gateway Class 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: + properties: + controllerName: + description: |- + ControllerName is the name of the Kubernetes controller + that manages Gateways of this class + type: string + description: + description: Description of GatewayClass + type: string + parametersRef: + description: |- + ParametersRef refers to a resource responsible for configuring + the behavior of the GatewayClass. + properties: + group: + description: The Kubernetes Group that the referred object belongs + to + type: string + kind: + description: The Kubernetes Kind that the referred object is + type: string + name: + description: The Name of the referred object + type: string + namespace: + description: The kubernetes namespace that the referred object + is in + type: string + required: + - name + type: object + required: + - controllerName + - parametersRef + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: |- + Conditions define a readiness condition for a Consul resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-gatewaypolicies.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-gatewaypolicies.yaml new file mode 100644 index 0000000000..904b65d607 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-gatewaypolicies.yaml @@ -0,0 +1,302 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: gatewaypolicies.consul.hashicorp.com +spec: + group: consul.hashicorp.com + names: + kind: GatewayPolicy + listKind: GatewayPolicyList + plural: gatewaypolicies + singular: gatewaypolicy + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: GatewayPolicy is the Schema for the gatewaypolicies 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: GatewayPolicySpec defines the desired state of GatewayPolicy. + properties: + default: + properties: + jwt: + description: GatewayJWTRequirement holds the list of JWT providers + to be verified against. + properties: + providers: + description: Providers is a list of providers to consider + when verifying a JWT. + items: + description: GatewayJWTProvider holds the provider and claim + verification information. + properties: + name: + description: |- + Name is the name of the JWT provider. There MUST be a corresponding + "jwt-provider" config entry with this name. + type: string + verifyClaims: + description: VerifyClaims is a list of additional claims + to verify in a JWT's payload. + items: + description: GatewayJWTClaimVerification holds the + actual claim information to be verified. + properties: + path: + description: Path is the path to the claim in + the token JSON. + items: + type: string + type: array + value: + description: |- + Value is the expected value at the given path: + - If the type at the path is a list then we verify + that this value is contained in the list. + + + - If the type at the path is a string then we verify + that this value matches. + type: string + required: + - path + - value + type: object + type: array + required: + - name + type: object + type: array + required: + - providers + type: object + type: object + override: + properties: + jwt: + description: GatewayJWTRequirement holds the list of JWT providers + to be verified against. + properties: + providers: + description: Providers is a list of providers to consider + when verifying a JWT. + items: + description: GatewayJWTProvider holds the provider and claim + verification information. + properties: + name: + description: |- + Name is the name of the JWT provider. There MUST be a corresponding + "jwt-provider" config entry with this name. + type: string + verifyClaims: + description: VerifyClaims is a list of additional claims + to verify in a JWT's payload. + items: + description: GatewayJWTClaimVerification holds the + actual claim information to be verified. + properties: + path: + description: Path is the path to the claim in + the token JSON. + items: + type: string + type: array + value: + description: |- + Value is the expected value at the given path: + - If the type at the path is a list then we verify + that this value is contained in the list. + + + - If the type at the path is a string then we verify + that this value matches. + type: string + required: + - path + - value + type: object + type: array + required: + - name + type: object + type: array + required: + - providers + type: object + type: object + targetRef: + description: TargetRef identifies an API object to apply policy to. + properties: + group: + description: Group is the group of the target resource. + maxLength: 253 + minLength: 1 + type: string + kind: + description: Kind is kind of the target resource. + maxLength: 253 + minLength: 1 + type: string + name: + description: Name is the name of the target resource. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. When unspecified, the local + namespace is inferred. Even when policy targets a resource in a different + namespace, it may only apply to traffic originating from the same + namespace as the policy. + maxLength: 253 + minLength: 1 + type: string + sectionName: + description: SectionName refers to the listener targeted by this + policy. + 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: + - group + - kind + - name + type: object + required: + - targetRef + type: object + status: + description: GatewayPolicyStatus defines the observed state of the gateway. + properties: + conditions: + description: |- + Conditions describe the current conditions of the Policy. + + + Known condition types are: + + + * "Accepted" + * "ResolvedRefs" + 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 + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-gateways-external.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-gateways-external.yaml new file mode 100644 index 0000000000..41df34942a --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-gateways-external.yaml @@ -0,0 +1,882 @@ +{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 + gateway.networking.k8s.io/bundle-version: v0.6.2 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + 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 + deprecated: true + deprecationWarning: The v1alpha2 version of Gateway has been deprecated and will be removed in a future release of the API. Please upgrade to v1beta1. + name: v1alpha2 + 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. \n 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. \n The .listener.hostname field is used to route traffic that has already arrived at the Gateway to the correct in-cluster destination. \n If no Addresses are specified, the implementation MAY schedule the Gateway in an implementation-specific manner, assigning an appropriate set of Addresses. \n The implementation MUST bind all Listeners to every GatewayAddress that it assigns to the Gateway and add a corresponding entry in GatewayStatus.Addresses. \n Support: Extended" + items: + description: GatewayAddress describes an address that can be bound to a Gateway. + 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. \n Examples: `1.2.3.4`, `128::1`, `my-ip-address`." + maxLength: 253 + minLength: 1 + type: string + required: + - value + type: object + maxItems: 16 + type: array + 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. \n Each listener in a Gateway must have a unique combination of Hostname, Port, and Protocol. \n An implementation MAY group Listeners by Port and then collapse each group of Listeners into a single Listener if the implementation determines that the Listeners in the group are \"compatible\". An implementation MAY also group together and collapse compatible Listeners belonging to different Gateways. \n For example, an implementation might consider Listeners to be compatible with each other if all of the following conditions are met: \n 1. Either each Listener within the group specifies the \"HTTP\" Protocol or each Listener within the group specifies either the \"HTTPS\" or \"TLS\" Protocol. \n 2. Each Listener within the group specifies a Hostname that is unique within the group. \n 3. As a special case, one Listener within a group may omit Hostname, in which case this Listener matches when no other Listener matches. \n If the implementation does collapse compatible Listeners, the hostname provided in the incoming client request MUST be matched to a Listener to find the correct set of Routes. The incoming hostname MUST be matched using the Hostname field for each Listener in order of most to least specific. That is, exact matches must be processed before wildcard matches. \n If this field specifies multiple Listeners that have the same Port value but are not compatible, the implementation must raise a \"Conflicted\" condition in the Listener status. \n 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. \n 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: \n * 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. \n 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. \n 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. \n 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. \n 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. \n 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. \n 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\". \n 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 + 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 + 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. \n Implementations MUST apply Hostname matching appropriately for each of the following protocols: \n * 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. \n 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. \n 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`. \n 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. \n 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. \n Support: Core" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + protocol: + description: "Protocol specifies the network protocol this listener expects to receive. \n 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\". \n The association of SNIs to Certificate defined in GatewayTLSConfig is defined based on the Hostname field for this listener. \n The GatewayClass MUST use the longest matching SNI out of all available certificates for any TLS handshake. \n 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. \n 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. \n 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. \n This field is required to have at least one element when the mode is set to \"Terminate\" (default) and is optional otherwise. \n CertificateRefs can reference to standard Kubernetes resources, i.e. Secret, or implementation-specific custom resources. \n Support: Core - A single reference to a Kubernetes Secret of type kubernetes.io/tls \n Support: Implementation-specific (More than one reference or other resource types)" + items: + description: "SecretObjectReference identifies an API object including its namespace, defaulting to Secret. \n 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. \n 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 "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 + namespace: + description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a 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. \n 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: \n - Terminate: The TLS session between the downstream client and the Gateway is terminated at the Gateway. This mode requires certificateRefs to be set and contain at least one element. - 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. CertificateRefs field is ignored in this mode. \n 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. \n 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. \n Support: Implementation-specific" + maxProperties: 16 + type: object + type: object + required: + - name + - port + - protocol + type: object + maxItems: 64 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + required: + - gatewayClassName + - listeners + type: object + status: + default: + conditions: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: NotReconciled + status: Unknown + type: Accepted + description: Status defines the current state of Gateway. + properties: + addresses: + description: Addresses lists the IP addresses that have actually been bound to the Gateway. These addresses may differ from the addresses in the Spec, e.g. if the Gateway automatically assigns an address from a reserved pool. + items: + description: GatewayAddress describes an address that can be bound to a Gateway. + 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. \n Examples: `1.2.3.4`, `128::1`, `my-ip-address`." + maxLength: 253 + minLength: 1 + type: string + required: + - value + type: object + 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. \n 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. \n Known condition types are: \n * \"Accepted\" * \"Ready\"" + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \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. + 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. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \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. \n 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: {} + - 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. \n 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. \n The .listener.hostname field is used to route traffic that has already arrived at the Gateway to the correct in-cluster destination. \n If no Addresses are specified, the implementation MAY schedule the Gateway in an implementation-specific manner, assigning an appropriate set of Addresses. \n The implementation MUST bind all Listeners to every GatewayAddress that it assigns to the Gateway and add a corresponding entry in GatewayStatus.Addresses. \n Support: Extended" + items: + description: GatewayAddress describes an address that can be bound to a Gateway. + 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. \n Examples: `1.2.3.4`, `128::1`, `my-ip-address`." + maxLength: 253 + minLength: 1 + type: string + required: + - value + type: object + maxItems: 16 + type: array + 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. \n Each listener in a Gateway must have a unique combination of Hostname, Port, and Protocol. \n An implementation MAY group Listeners by Port and then collapse each group of Listeners into a single Listener if the implementation determines that the Listeners in the group are \"compatible\". An implementation MAY also group together and collapse compatible Listeners belonging to different Gateways. \n For example, an implementation might consider Listeners to be compatible with each other if all of the following conditions are met: \n 1. Either each Listener within the group specifies the \"HTTP\" Protocol or each Listener within the group specifies either the \"HTTPS\" or \"TLS\" Protocol. \n 2. Each Listener within the group specifies a Hostname that is unique within the group. \n 3. As a special case, one Listener within a group may omit Hostname, in which case this Listener matches when no other Listener matches. \n If the implementation does collapse compatible Listeners, the hostname provided in the incoming client request MUST be matched to a Listener to find the correct set of Routes. The incoming hostname MUST be matched using the Hostname field for each Listener in order of most to least specific. That is, exact matches must be processed before wildcard matches. \n If this field specifies multiple Listeners that have the same Port value but are not compatible, the implementation must raise a \"Conflicted\" condition in the Listener status. \n 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. \n 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: \n * 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. \n 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. \n 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. \n 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. \n 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. \n 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. \n 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\". \n 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 + 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 + 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. \n Implementations MUST apply Hostname matching appropriately for each of the following protocols: \n * 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. \n 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. \n 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`. \n 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. \n 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. \n Support: Core" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + protocol: + description: "Protocol specifies the network protocol this listener expects to receive. \n 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\". \n The association of SNIs to Certificate defined in GatewayTLSConfig is defined based on the Hostname field for this listener. \n The GatewayClass MUST use the longest matching SNI out of all available certificates for any TLS handshake. \n 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. \n 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. \n 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. \n This field is required to have at least one element when the mode is set to \"Terminate\" (default) and is optional otherwise. \n CertificateRefs can reference to standard Kubernetes resources, i.e. Secret, or implementation-specific custom resources. \n Support: Core - A single reference to a Kubernetes Secret of type kubernetes.io/tls \n Support: Implementation-specific (More than one reference or other resource types)" + items: + description: "SecretObjectReference identifies an API object including its namespace, defaulting to Secret. \n 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. \n 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 "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 + namespace: + description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a 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. \n 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: \n - Terminate: The TLS session between the downstream client and the Gateway is terminated at the Gateway. This mode requires certificateRefs to be set and contain at least one element. - 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. CertificateRefs field is ignored in this mode. \n 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. \n 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. \n Support: Implementation-specific" + maxProperties: 16 + type: object + type: object + required: + - name + - port + - protocol + type: object + maxItems: 64 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + required: + - gatewayClassName + - listeners + type: object + status: + default: + conditions: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: NotReconciled + status: Unknown + type: Accepted + description: Status defines the current state of Gateway. + properties: + addresses: + description: Addresses lists the IP addresses that have actually been bound to the Gateway. These addresses may differ from the addresses in the Spec, e.g. if the Gateway automatically assigns an address from a reserved pool. + items: + description: GatewayAddress describes an address that can be bound to a Gateway. + 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. \n Examples: `1.2.3.4`, `128::1`, `my-ip-address`." + maxLength: 253 + minLength: 1 + type: string + required: + - value + type: object + 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. \n 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. \n Known condition types are: \n * \"Accepted\" * \"Ready\"" + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \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. + 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. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \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. \n 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: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-grpcroutes-external.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-grpcroutes-external.yaml new file mode 100644 index 0000000000..739ed2c659 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-grpcroutes-external.yaml @@ -0,0 +1,766 @@ +{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 + gateway.networking.k8s.io/bundle-version: v0.6.2 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + 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: 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. \n 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. \n 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. \n 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. \n Support: Extended" + 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: \n 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. \n 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: \n * 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. \n 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`. \n 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. \n 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. \n 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: \n * The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by \"{namespace}/{name}\". \n The rejected Route MUST raise an 'Accepted' condition with a status of 'False' in the corresponding RouteParentStatus. \n 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: \n 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. \n 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`). \n 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. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n 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. \n 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 any other kind 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). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n 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). \n 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. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n 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. \n 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. \n 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. \n 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. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n 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. \n Support: Extended \n " + 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: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n 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. \n 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 + rules: + default: + - matches: + - method: + type: Exact + description: Rules are a list of GRPC matchers, filters and actions. + items: + description: GRPCRouteRule defines the semantics for matching an 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. \n Failure behavior here depends on how many BackendRefs are specified and how many are invalid. \n 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. \n See the GRPCBackendRef definition for the rules about what makes a single GRPCBackendRef invalid. \n 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. \n 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. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Core" + items: + description: GRPCBackendRef defines how a GRPCRoute forwards a gRPC request. + 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. \n 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. \n 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. \n 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. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n 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). \n 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). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n 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). \n 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. \n Support: Extended" + properties: + backendRef: + description: "BackendRef references a resource where mirrored requests are sent. \n 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. \n 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. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n 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 kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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. \n Note that when a 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. \n 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 + required: + - backendRef + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " + 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. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n 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). \n 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). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n 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). \n 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: \n - 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. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - 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. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. \n 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. \n " + enum: + - ResponseHeaderModifier + - RequestHeaderModifier + - RequestMirror + - ExtensionRef + type: string + required: + - type + type: object + maxItems: 16 + type: array + 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 kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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. \n Note that when a 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. \n 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. \n 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. \n Support for this field varies based on the context where used." + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + maxItems: 16 + type: array + filters: + description: "Filters define the filters that are applied to requests that match this rule. \n The effects of ordering of multiple behaviors are currently unspecified. This can change in the future based on feedback during the alpha stage. \n Conformance-levels at this level are defined based on the type of filter: \n - 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. \n Specifying a core filter multiple times has unspecified or implementation-specific conformance. 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. \n 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. \n 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. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n 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). \n 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). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n 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). \n 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. \n Support: Extended" + properties: + backendRef: + description: "BackendRef references a resource where mirrored requests are sent. \n 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. \n 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. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n 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 kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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. \n Note that when a 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. \n 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 + required: + - backendRef + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " + 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. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n 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). \n 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). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n 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). \n 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: \n - 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. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - 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. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. \n 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. \n " + enum: + - ResponseHeaderModifier + - RequestHeaderModifier + - RequestMirror + - ExtensionRef + type: string + required: + - type + type: object + maxItems: 16 + type: array + matches: + default: + - method: + type: Exact + 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. \n For example, take the following matches configuration: \n ``` matches: - method: service: foo.bar headers: values: version: 2 - method: service: foo.bar.v2 ``` \n For a request to match against this rule, it MUST satisfy EITHER of the two conditions: \n - service of foo.bar AND contains the header `version: 2` - service of foo.bar.v2 \n See the documentation for GRPCRouteMatch on how to specify multiple match conditions to be ANDed together. \n If no matches are specified, the implementation MUST match every gRPC request. \n 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: \n * Characters in a matching non-wildcard hostname. * Characters in a matching hostname. * Characters in a matching service. * Characters in a matching method. * Header matches. \n If ties still exist across multiple Routes, matching precedence MUST be determined in order of the following criteria, continuing on ties: \n * The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by \"{namespace}/{name}\". \n 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. \n For example, the match below will match a gRPC request only if its service is `foo` AND it contains the `version: v1` header: \n ``` matches: - method: type: Exact service: \"foo\" headers: - name: \"version\" value \"v1\" \n ```" + 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. \n 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: + default: + type: Exact + 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. \n At least one of Service and Method MUST be a non-empty string. \n A GRPC Method must be a valid Protobuf Method (https://protobuf.com/docs/language-spec#methods)." + maxLength: 1024 + pattern: ^[A-Za-z_][A-Za-z_0-9]*$ + type: string + service: + description: "Value of the service to match against. If left empty or omitted, will match any service. \n At least one of Service and Method MUST be a non-empty string. \n A GRPC Service must be a valid Protobuf Type Name (https://protobuf.com/docs/language-spec#type-references)." + maxLength: 1024 + pattern: ^(?i)\.?[a-z_][a-z_0-9]*(\.[a-z_][a-z_0-9]*)*$ + 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) \n Support: Implementation-specific (Exact with method specified but no service specified) \n Support: Implementation-specific (RegularExpression)" + enum: + - Exact + - RegularExpression + type: string + type: object + 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. \n 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. \n 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. \n 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. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * 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. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \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. \n Example: \"example.net/gateway-controller\". \n 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). \n 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). \n 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. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n 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. \n 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. \n 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. \n 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. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n 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. \n Support: Extended \n " + 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: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n 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. \n 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: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-grpcroutes.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-grpcroutes.yaml new file mode 100644 index 0000000000..8d2c61c75e --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-grpcroutes.yaml @@ -0,0 +1,669 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: grpcroutes.mesh.consul.hashicorp.com +spec: + group: mesh.consul.hashicorp.com + names: + kind: GRPCRoute + listKind: GRPCRouteList + plural: grpcroutes + shortNames: + - grpc-route + singular: grpcroute + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v2beta1 + schema: + openAPIV3Schema: + description: GRPCRoute is the Schema for the GRPC Route 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: |- + NOTE: this should align to the GAMMA/gateway-api version, or at least be + easily translatable. + + + https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRoute + + + This is a Resource type. + properties: + hostnames: + description: |- + Hostnames are the hostnames for which this GRPCRoute should respond to requests. + + + This is only valid for north/south. + items: + type: string + type: array + parentRefs: + description: |- + ParentRefs references the resources (usually Services) that a Route wants + to be attached to. + + + It is invalid to reference an identical parent more than once. It is valid + to reference multiple distinct sections within the same parent resource. + items: + description: 'NOTE: roughly equivalent to structs.ResourceReference' + properties: + port: + description: |- + For east/west this is the name of the Consul Service port to direct traffic to + or empty to imply all. + For north/south this is TBD. + + + For more details on potential values of this field, see documentation for + Service.ServicePort. + type: string + ref: + description: |- + For east/west configuration, this should point to a Service. + For north/south it should point to a Gateway. + properties: + name: + description: Name is the user-given name of the resource + (e.g. the "billing" service). + type: string + section: + description: Section identifies which part of the resource + the condition relates to. + type: string + tenancy: + description: |- + Tenancy identifies the tenancy units (i.e. partition, namespace) in which + the resource resides. + properties: + namespace: + description: |- + Namespace further isolates resources within a partition. + https://developer.hashicorp.com/consul/docs/enterprise/namespaces + + + When using the List and WatchList endpoints, provide the wildcard value "*" + to list resources across all namespaces. + type: string + partition: + description: |- + Partition is the topmost administrative boundary within a cluster. + https://developer.hashicorp.com/consul/docs/enterprise/admin-partitions + + + When using the List and WatchList endpoints, provide the wildcard value "*" + to list resources across all partitions. + type: string + type: object + type: + description: Type identifies the resource's type. + properties: + group: + description: |- + Group describes the area of functionality to which this resource type + relates (e.g. "catalog", "authorization"). + type: string + groupVersion: + description: |- + GroupVersion is incremented when sweeping or backward-incompatible changes + are made to the group's resource types. + type: string + kind: + description: Kind identifies the specific resource type + within the group. + type: string + type: object + type: object + type: object + type: array + rules: + description: Rules are a list of GRPC matchers, filters and actions. + items: + 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 GRPCBackendRef definition for the rules about what makes a single + GRPCBackendRef invalid. + + + When a GRPCBackendRef 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. + items: + properties: + backendRef: + properties: + datacenter: + type: string + port: + description: |- + For east/west this is the name of the Consul Service port to direct traffic to + or empty to imply using the same value as the parent ref. + For north/south this is TBD. + + + For more details on potential values of this field, see documentation for + Service.ServicePort. + type: string + ref: + description: For east/west configuration, this should + point to a Service. + properties: + name: + description: Name is the user-given name of the + resource (e.g. the "billing" service). + type: string + section: + description: Section identifies which part of + the resource the condition relates to. + type: string + tenancy: + description: |- + Tenancy identifies the tenancy units (i.e. partition, namespace) in which + the resource resides. + properties: + namespace: + description: |- + Namespace further isolates resources within a partition. + https://developer.hashicorp.com/consul/docs/enterprise/namespaces + + + When using the List and WatchList endpoints, provide the wildcard value "*" + to list resources across all namespaces. + type: string + partition: + description: |- + Partition is the topmost administrative boundary within a cluster. + https://developer.hashicorp.com/consul/docs/enterprise/admin-partitions + + + When using the List and WatchList endpoints, provide the wildcard value "*" + to list resources across all partitions. + type: string + type: object + type: + description: Type identifies the resource's type. + properties: + group: + description: |- + Group describes the area of functionality to which this resource type + relates (e.g. "catalog", "authorization"). + type: string + groupVersion: + description: |- + GroupVersion is incremented when sweeping or backward-incompatible changes + are made to the group's resource types. + type: string + kind: + description: Kind identifies the specific + resource type within the group. + type: string + type: object + type: object + type: object + filters: + description: |- + Filters defined at this level should be executed if and only if the + request is being forwarded to the backend defined here. + items: + properties: + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. + 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. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + 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). + items: + type: string + type: array + set: + description: |- + Set overwrites the request with the given header (name, value) before the + action. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies + response headers. + 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. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + 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). + items: + type: string + type: array + set: + description: |- + Set overwrites the request with the given header (name, value) before the + action. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + type: object + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during + forwarding. + properties: + pathPrefix: + type: string + type: object + type: object + type: array + weight: + 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. + format: int32 + type: integer + type: object + type: array + filters: + items: + properties: + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. + 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. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + 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). + items: + type: string + type: array + set: + description: |- + Set overwrites the request with the given header (name, value) before the + action. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies + response headers. + 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. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + 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). + items: + type: string + type: array + set: + description: |- + Set overwrites the request with the given header (name, value) before the + action. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + type: object + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during + forwarding. + properties: + pathPrefix: + type: string + type: object + type: object + type: array + matches: + items: + 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: + properties: + name: + type: string + type: + description: |- + HeaderMatchType specifies the semantics of how HTTP header values should be + compared. Valid HeaderMatchType values, along with their conformance levels, + are: + + + 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: + - HEADER_MATCH_TYPE_UNSPECIFIED + - HEADER_MATCH_TYPE_EXACT + - HEADER_MATCH_TYPE_REGEX + - HEADER_MATCH_TYPE_PRESENT + - HEADER_MATCH_TYPE_PREFIX + - HEADER_MATCH_TYPE_SUFFIX + format: int32 + type: string + value: + type: string + type: object + type: array + 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.} + 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. + type: string + type: + description: |- + Type specifies how to match against the service and/or method. Support: + Core (Exact with service and method specified) + enum: + - GRPC_METHOD_MATCH_TYPE_UNSPECIFIED + - GRPC_METHOD_MATCH_TYPE_EXACT + - GRPC_METHOD_MATCH_TYPE_REGEX + format: int32 + type: string + type: object + type: object + type: array + retries: + properties: + number: + description: |- + Number is the number of times to retry the request when a retryable + result occurs. + properties: + value: + description: The uint32 value. + format: int32 + type: integer + type: object + onConditions: + description: |- + RetryOn allows setting envoy specific conditions when a request should + be automatically retried. + items: + type: string + type: array + onConnectFailure: + description: |- + RetryOnConnectFailure allows for connection failure errors to trigger a + retry. + type: boolean + onStatusCodes: + description: |- + RetryOnStatusCodes is a flat list of http response status codes that are + eligible for retry. This again should be feasible in any reasonable proxy. + items: + format: int32 + type: integer + type: array + type: object + timeouts: + description: |- + HTTPRouteTimeouts defines timeouts that can be configured for an HTTPRoute + or GRPCRoute. + properties: + idle: + description: Idle specifies the total amount of time permitted + for the request stream to be idle. + format: duration + properties: + nanos: + description: |- + Signed fractions of a second at nanosecond resolution of the span + of time. Durations less than one second are represented with a 0 + `seconds` field and a positive or negative `nanos` field. For durations + of one second or more, a non-zero value for the `nanos` field must be + of the same sign as the `seconds` field. Must be from -999,999,999 + to +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: |- + Signed seconds of the span of time. Must be from -315,576,000,000 + to +315,576,000,000 inclusive. Note: these bounds are computed from: + 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years + format: int64 + type: integer + type: object + request: + description: |- + RequestTimeout is the total amount of time permitted for the entire + downstream request (and retries) to be processed. + format: duration + properties: + nanos: + description: |- + Signed fractions of a second at nanosecond resolution of the span + of time. Durations less than one second are represented with a 0 + `seconds` field and a positive or negative `nanos` field. For durations + of one second or more, a non-zero value for the `nanos` field must be + of the same sign as the `seconds` field. Must be from -999,999,999 + to +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: |- + Signed seconds of the span of time. Must be from -315,576,000,000 + to +315,576,000,000 inclusive. Note: these bounds are computed from: + 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years + format: int64 + type: integer + type: object + type: object + type: object + type: array + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: |- + Conditions define a readiness condition for a Consul resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-httproutes-external.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-httproutes-external.yaml new file mode 100644 index 0000000000..bba3672d16 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-httproutes-external.yaml @@ -0,0 +1,1914 @@ +{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 + gateway.networking.k8s.io/bundle-version: v0.6.2 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + 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 + deprecated: true + deprecationWarning: The v1alpha2 version of HTTPRoute has been deprecated and will be removed in a future release of the API. Please upgrade to v1beta1. + name: v1alpha2 + 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 hostname that should match against the HTTP Host header to select a HTTPRoute to process the request. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 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. \n 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: \n * 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. \n 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`. \n 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. \n 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. \n 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: \n * Characters in a matching non-wildcard hostname. * Characters in a matching hostname. \n If ties exist across multiple Routes, the matching precedence rules for HTTPRouteMatches takes over. \n 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: \n 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. \n 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`). \n 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. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n 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. \n 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 any other kind 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). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n 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). \n 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. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n 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. \n 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. \n 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. \n 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. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n 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. \n Support: Extended \n " + 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: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n 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. \n 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 + 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. \n Failure behavior here depends on how many BackendRefs are specified and how many are invalid. \n 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. \n See the HTTPBackendRef definition for the rules about what makes a single HTTPBackendRef invalid. \n 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. \n 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. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Core" + items: + description: HTTPBackendRef defines how a HTTPRoute should forward an HTTP request. + 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. \n 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. \n 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. \n 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. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n 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). \n 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). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n 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). \n 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. \n Support: Extended" + properties: + backendRef: + description: "BackendRef references a resource where mirrored requests are sent. \n 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. \n 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. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n 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 kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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. \n Note that when a 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. \n 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 + required: + - backendRef + type: object + requestRedirect: + description: "RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. \n 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 of the request is used. \n 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. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + 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\" would be modified to \"/bar\". \n 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. \n " + 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. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: "Port is the port to be used in the value of the `Location` header in the response. When empty, port (if specified) of the request is used. \n 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. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status code to be used in response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Core" + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " + 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. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n 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). \n 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). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n 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). \n 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: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations must support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - 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. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. \n 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. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: "URLRewrite defines a schema for a filter that modifies a request during forwarding. \n Support: Extended \n " + properties: + hostname: + description: "Hostname is the value to be used to replace the Host header value during forwarding. \n Support: Extended \n " + 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. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + 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\" would be modified to \"/bar\". \n 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. \n " + 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. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + 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 kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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. \n Note that when a 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. \n 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. \n 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. \n Support for this field varies based on the context where used." + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + maxItems: 16 + type: array + filters: + description: "Filters define the filters that are applied to requests that match this rule. \n The effects of ordering of multiple behaviors are currently unspecified. This can change in the future based on feedback during the alpha stage. \n Conformance-levels at this level are defined based on the type of filter: \n - 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. \n Specifying a core filter multiple times has unspecified or implementation-specific conformance. \n 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 all cases where incompatible or unsupported filters are specified, implementations MUST add a warning condition to status. \n 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. \n 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. \n 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. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n 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). \n 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). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n 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). \n 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. \n Support: Extended" + properties: + backendRef: + description: "BackendRef references a resource where mirrored requests are sent. \n 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. \n 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. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n 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 kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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. \n Note that when a 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. \n 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 + required: + - backendRef + type: object + requestRedirect: + description: "RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. \n 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 of the request is used. \n 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. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + 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\" would be modified to \"/bar\". \n 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. \n " + 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. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: "Port is the port to be used in the value of the `Location` header in the response. When empty, port (if specified) of the request is used. \n 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. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status code to be used in response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Core" + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " + 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. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n 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). \n 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). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n 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). \n 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: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations must support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - 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. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. \n 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. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: "URLRewrite defines a schema for a filter that modifies a request during forwarding. \n Support: Extended \n " + properties: + hostname: + description: "Hostname is the value to be used to replace the Host header value during forwarding. \n Support: Extended \n " + 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. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + 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\" would be modified to \"/bar\". \n 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. \n " + 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. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + 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. \n For example, take the following matches configuration: \n ``` matches: - path: value: \"/foo\" headers: - name: \"version\" value: \"v2\" - path: value: \"/v2/foo\" ``` \n For a request to match against this rule, a request must satisfy EITHER of the two conditions: \n - path prefixed with `/foo` AND contains the header `version: v2` - path prefix of `/v2/foo` \n See the documentation for HTTPRouteMatch on how to specify multiple match conditions that should be ANDed together. \n If no matches are specified, the default is a prefix path match on \"/\", which has the effect of matching every HTTP request. \n 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 with the largest number of: \n * Characters in a matching path. * Header matches. * Query param matches. \n If ties still exist across multiple Routes, matching precedence MUST be determined in order of the following criteria, continuing on ties: \n * The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by \"{namespace}/{name}\". \n 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. \n 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 action. Multiple match types are ANDed together, i.e. the match will evaluate to true only if all conditions are satisfied. \n For example, the match below will match a HTTP request only if its path starts with `/foo` AND it contains the `version: v1` header: \n ``` match: \n \tpath: \t value: \"/foo\" \theaders: \t- name: \"version\" \t value \"v1\" \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). \n 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. \n 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. \n Support: Core (Exact) \n Support: Implementation-specific (RegularExpression) \n 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. \n 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. \n Support: Core (Exact, PathPrefix) \n 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 + 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. \n 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). \n 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. \n 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. \n Users SHOULD NOT route traffic based on repeated query params to guard themselves against potential differences in the implementations." + maxLength: 256 + minLength: 1 + type: string + type: + default: Exact + description: "Type specifies how to match against the value of the query parameter. \n Support: Extended (Exact) \n Support: Implementation-specific (RegularExpression) \n 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 + 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. \n 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. \n 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. \n 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. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * 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. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \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. \n Example: \"example.net/gateway-controller\". \n 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). \n 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). \n 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. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n 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. \n 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. \n 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. \n 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. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n 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. \n Support: Extended \n " + 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: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n 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. \n 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: {} + - 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 hostname that should match against the HTTP Host header to select a HTTPRoute to process the request. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 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. \n 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: \n * 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. \n 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`. \n 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. \n 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. \n 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: \n * Characters in a matching non-wildcard hostname. * Characters in a matching hostname. \n If ties exist across multiple Routes, the matching precedence rules for HTTPRouteMatches takes over. \n 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: \n 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. \n 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`). \n 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. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n 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. \n 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 any other kind 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). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n 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). \n 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. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n 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. \n 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. \n 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. \n 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. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n 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. \n Support: Extended \n " + 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: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n 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. \n 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 + 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. \n Failure behavior here depends on how many BackendRefs are specified and how many are invalid. \n 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. \n See the HTTPBackendRef definition for the rules about what makes a single HTTPBackendRef invalid. \n 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. \n 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. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Core" + items: + description: HTTPBackendRef defines how a HTTPRoute should forward an HTTP request. + 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. \n 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. \n 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. \n 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. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n 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). \n 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). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n 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). \n 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. \n Support: Extended" + properties: + backendRef: + description: "BackendRef references a resource where mirrored requests are sent. \n 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. \n 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. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n 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 kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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. \n Note that when a 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. \n 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 + required: + - backendRef + type: object + requestRedirect: + description: "RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. \n 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 of the request is used. \n 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. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + 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\" would be modified to \"/bar\". \n 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. \n " + 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. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: "Port is the port to be used in the value of the `Location` header in the response. When empty, port (if specified) of the request is used. \n 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. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status code to be used in response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Core" + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " + 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. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n 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). \n 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). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n 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). \n 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: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations must support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - 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. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. \n 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. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: "URLRewrite defines a schema for a filter that modifies a request during forwarding. \n Support: Extended \n " + properties: + hostname: + description: "Hostname is the value to be used to replace the Host header value during forwarding. \n Support: Extended \n " + 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. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + 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\" would be modified to \"/bar\". \n 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. \n " + 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. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + 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 kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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. \n Note that when a 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. \n 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. \n 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. \n Support for this field varies based on the context where used." + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + maxItems: 16 + type: array + filters: + description: "Filters define the filters that are applied to requests that match this rule. \n The effects of ordering of multiple behaviors are currently unspecified. This can change in the future based on feedback during the alpha stage. \n Conformance-levels at this level are defined based on the type of filter: \n - 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. \n Specifying a core filter multiple times has unspecified or implementation-specific conformance. \n 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 all cases where incompatible or unsupported filters are specified, implementations MUST add a warning condition to status. \n 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. \n 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. \n 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. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n 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). \n 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). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n 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). \n 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. \n Support: Extended" + properties: + backendRef: + description: "BackendRef references a resource where mirrored requests are sent. \n 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. \n 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. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n 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 kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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. \n Note that when a 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. \n 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 + required: + - backendRef + type: object + requestRedirect: + description: "RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. \n 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 of the request is used. \n 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. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + 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\" would be modified to \"/bar\". \n 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. \n " + 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. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: "Port is the port to be used in the value of the `Location` header in the response. When empty, port (if specified) of the request is used. \n 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. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status code to be used in response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Core" + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " + 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. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n 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). \n 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). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n 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). \n 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: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations must support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - 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. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. \n 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. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: "URLRewrite defines a schema for a filter that modifies a request during forwarding. \n Support: Extended \n " + properties: + hostname: + description: "Hostname is the value to be used to replace the Host header value during forwarding. \n Support: Extended \n " + 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. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + 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\" would be modified to \"/bar\". \n 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. \n " + 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. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + 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. \n For example, take the following matches configuration: \n ``` matches: - path: value: \"/foo\" headers: - name: \"version\" value: \"v2\" - path: value: \"/v2/foo\" ``` \n For a request to match against this rule, a request must satisfy EITHER of the two conditions: \n - path prefixed with `/foo` AND contains the header `version: v2` - path prefix of `/v2/foo` \n See the documentation for HTTPRouteMatch on how to specify multiple match conditions that should be ANDed together. \n If no matches are specified, the default is a prefix path match on \"/\", which has the effect of matching every HTTP request. \n 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 with the largest number of: \n * Characters in a matching path. * Header matches. * Query param matches. \n If ties still exist across multiple Routes, matching precedence MUST be determined in order of the following criteria, continuing on ties: \n * The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by \"{namespace}/{name}\". \n 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. \n 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 action. Multiple match types are ANDed together, i.e. the match will evaluate to true only if all conditions are satisfied. \n For example, the match below will match a HTTP request only if its path starts with `/foo` AND it contains the `version: v1` header: \n ``` match: \n \tpath: \t value: \"/foo\" \theaders: \t- name: \"version\" \t value \"v1\" \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). \n 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. \n 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. \n Support: Core (Exact) \n Support: Implementation-specific (RegularExpression) \n 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. \n 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. \n Support: Core (Exact, PathPrefix) \n 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 + 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. \n 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). \n 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. \n 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. \n Users SHOULD NOT route traffic based on repeated query params to guard themselves against potential differences in the implementations." + maxLength: 256 + minLength: 1 + type: string + type: + default: Exact + description: "Type specifies how to match against the value of the query parameter. \n Support: Extended (Exact) \n Support: Implementation-specific (RegularExpression) \n 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 + 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. \n 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. \n 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. \n 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. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * 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. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \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. \n Example: \"example.net/gateway-controller\". \n 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). \n 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). \n 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. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n 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. \n 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. \n 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. \n 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. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n 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. \n Support: Extended \n " + 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: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n 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. \n 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: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-httproutes.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-httproutes.yaml new file mode 100644 index 0000000000..a782647534 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-httproutes.yaml @@ -0,0 +1,726 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: httproutes.mesh.consul.hashicorp.com +spec: + group: mesh.consul.hashicorp.com + names: + kind: HTTPRoute + listKind: HTTPRouteList + plural: httproutes + shortNames: + - http-route + singular: httproute + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v2beta1 + schema: + openAPIV3Schema: + description: HTTPRoute is the Schema for the HTTP Route 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: |- + NOTE: this should align to the GAMMA/gateway-api version, or at least be + easily translatable. + + + https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.HTTPRoute + + + This is a Resource type. + properties: + hostnames: + description: |- + Hostnames are the hostnames for which this HTTPRoute should respond to requests. + + + This is only valid for north/south. + items: + type: string + type: array + parentRefs: + description: |- + ParentRefs references the resources (usually Services) that a Route wants + to be attached to. + + + It is invalid to reference an identical parent more than once. It is valid + to reference multiple distinct sections within the same parent resource. + items: + description: 'NOTE: roughly equivalent to structs.ResourceReference' + properties: + port: + description: |- + For east/west this is the name of the Consul Service port to direct traffic to + or empty to imply all. + For north/south this is TBD. + + + For more details on potential values of this field, see documentation for + Service.ServicePort. + type: string + ref: + description: |- + For east/west configuration, this should point to a Service. + For north/south it should point to a Gateway. + properties: + name: + description: Name is the user-given name of the resource + (e.g. the "billing" service). + type: string + section: + description: Section identifies which part of the resource + the condition relates to. + type: string + tenancy: + description: |- + Tenancy identifies the tenancy units (i.e. partition, namespace) in which + the resource resides. + properties: + namespace: + description: |- + Namespace further isolates resources within a partition. + https://developer.hashicorp.com/consul/docs/enterprise/namespaces + + + When using the List and WatchList endpoints, provide the wildcard value "*" + to list resources across all namespaces. + type: string + partition: + description: |- + Partition is the topmost administrative boundary within a cluster. + https://developer.hashicorp.com/consul/docs/enterprise/admin-partitions + + + When using the List and WatchList endpoints, provide the wildcard value "*" + to list resources across all partitions. + type: string + type: object + type: + description: Type identifies the resource's type. + properties: + group: + description: |- + Group describes the area of functionality to which this resource type + relates (e.g. "catalog", "authorization"). + type: string + groupVersion: + description: |- + GroupVersion is incremented when sweeping or backward-incompatible changes + are made to the group's resource types. + type: string + kind: + description: Kind identifies the specific resource type + within the group. + type: string + type: object + type: object + type: object + type: array + rules: + description: |- + Rules are a list of HTTP-based routing rules that this route should + use for constructing a routing table. + items: + description: |- + HTTPRouteRule specifies the routing rules used to determine what upstream + service an HTTP request is routed to. + 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. + items: + properties: + backendRef: + properties: + datacenter: + type: string + port: + description: |- + For east/west this is the name of the Consul Service port to direct traffic to + or empty to imply using the same value as the parent ref. + For north/south this is TBD. + + + For more details on potential values of this field, see documentation for + Service.ServicePort. + type: string + ref: + description: For east/west configuration, this should + point to a Service. + properties: + name: + description: Name is the user-given name of the + resource (e.g. the "billing" service). + type: string + section: + description: Section identifies which part of + the resource the condition relates to. + type: string + tenancy: + description: |- + Tenancy identifies the tenancy units (i.e. partition, namespace) in which + the resource resides. + properties: + namespace: + description: |- + Namespace further isolates resources within a partition. + https://developer.hashicorp.com/consul/docs/enterprise/namespaces + + + When using the List and WatchList endpoints, provide the wildcard value "*" + to list resources across all namespaces. + type: string + partition: + description: |- + Partition is the topmost administrative boundary within a cluster. + https://developer.hashicorp.com/consul/docs/enterprise/admin-partitions + + + When using the List and WatchList endpoints, provide the wildcard value "*" + to list resources across all partitions. + type: string + type: object + type: + description: Type identifies the resource's type. + properties: + group: + description: |- + Group describes the area of functionality to which this resource type + relates (e.g. "catalog", "authorization"). + type: string + groupVersion: + description: |- + GroupVersion is incremented when sweeping or backward-incompatible changes + are made to the group's resource types. + type: string + kind: + description: Kind identifies the specific + resource type within the group. + type: string + type: object + type: object + type: object + filters: + description: |- + Filters defined at this level should be executed if and only if the + request is being forwarded to the backend defined here. + items: + properties: + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. + 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. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + 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). + items: + type: string + type: array + set: + description: |- + Set overwrites the request with the given header (name, value) before the + action. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies + response headers. + 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. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + 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). + items: + type: string + type: array + set: + description: |- + Set overwrites the request with the given header (name, value) before the + action. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + type: object + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during + forwarding. + properties: + pathPrefix: + type: string + type: object + type: object + type: array + weight: + 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. + format: int32 + type: integer + type: object + type: array + filters: + items: + properties: + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. + 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. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + 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). + items: + type: string + type: array + set: + description: |- + Set overwrites the request with the given header (name, value) before the + action. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies + response headers. + 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. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + 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). + items: + type: string + type: array + set: + description: |- + Set overwrites the request with the given header (name, value) before the + action. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + type: object + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during + forwarding. + properties: + pathPrefix: + type: string + type: object + type: object + type: array + matches: + items: + 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: + properties: + invert: + description: 'NOTE: not in gamma; service-router + compat' + type: boolean + 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”. + type: string + type: + description: Type specifies how to match against + the value of the header. + enum: + - HEADER_MATCH_TYPE_UNSPECIFIED + - HEADER_MATCH_TYPE_EXACT + - HEADER_MATCH_TYPE_REGEX + - HEADER_MATCH_TYPE_PRESENT + - HEADER_MATCH_TYPE_PREFIX + - HEADER_MATCH_TYPE_SUFFIX + format: int32 + type: string + value: + description: Value is the value of HTTP Header to + be matched. + type: string + type: object + type: array + method: + description: |- + Method specifies HTTP method matcher. When specified, this route will be + matched only if the request has the specified method. + type: string + path: + 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: + description: Type specifies how to match against the + path Value. + enum: + - PATH_MATCH_TYPE_UNSPECIFIED + - PATH_MATCH_TYPE_EXACT + - PATH_MATCH_TYPE_PREFIX + - PATH_MATCH_TYPE_REGEX + format: int32 + type: string + value: + description: Value of the HTTP path to match against. + type: string + type: object + 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. + items: + 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. + type: string + type: + description: Type specifies how to match against + the value of the query parameter. + enum: + - QUERY_PARAM_MATCH_TYPE_UNSPECIFIED + - QUERY_PARAM_MATCH_TYPE_EXACT + - QUERY_PARAM_MATCH_TYPE_REGEX + - QUERY_PARAM_MATCH_TYPE_PRESENT + format: int32 + type: string + value: + description: Value is the value of HTTP query param + to be matched. + type: string + type: object + type: array + type: object + type: array + retries: + properties: + number: + description: |- + Number is the number of times to retry the request when a retryable + result occurs. + properties: + value: + description: The uint32 value. + format: int32 + type: integer + type: object + onConditions: + description: |- + RetryOn allows setting envoy specific conditions when a request should + be automatically retried. + items: + type: string + type: array + onConnectFailure: + description: |- + RetryOnConnectFailure allows for connection failure errors to trigger a + retry. + type: boolean + onStatusCodes: + description: |- + RetryOnStatusCodes is a flat list of http response status codes that are + eligible for retry. This again should be feasible in any reasonable proxy. + items: + format: int32 + type: integer + type: array + type: object + timeouts: + description: |- + HTTPRouteTimeouts defines timeouts that can be configured for an HTTPRoute + or GRPCRoute. + properties: + idle: + description: Idle specifies the total amount of time permitted + for the request stream to be idle. + format: duration + properties: + nanos: + description: |- + Signed fractions of a second at nanosecond resolution of the span + of time. Durations less than one second are represented with a 0 + `seconds` field and a positive or negative `nanos` field. For durations + of one second or more, a non-zero value for the `nanos` field must be + of the same sign as the `seconds` field. Must be from -999,999,999 + to +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: |- + Signed seconds of the span of time. Must be from -315,576,000,000 + to +315,576,000,000 inclusive. Note: these bounds are computed from: + 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years + format: int64 + type: integer + type: object + request: + description: |- + RequestTimeout is the total amount of time permitted for the entire + downstream request (and retries) to be processed. + format: duration + properties: + nanos: + description: |- + Signed fractions of a second at nanosecond resolution of the span + of time. Durations less than one second are represented with a 0 + `seconds` field and a positive or negative `nanos` field. For durations + of one second or more, a non-zero value for the `nanos` field must be + of the same sign as the `seconds` field. Must be from -999,999,999 + to +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: |- + Signed seconds of the span of time. Must be from -315,576,000,000 + to +315,576,000,000 inclusive. Note: these bounds are computed from: + 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years + format: int64 + type: integer + type: object + type: object + type: object + type: array + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: |- + Conditions define a readiness condition for a Consul resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-ingressgateways.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-ingressgateways.yaml new file mode 100644 index 0000000000..53649c8667 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-ingressgateways.yaml @@ -0,0 +1,466 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: ingressgateways.consul.hashicorp.com +spec: + group: consul.hashicorp.com + names: + kind: IngressGateway + listKind: IngressGatewayList + plural: ingressgateways + shortNames: + - ingress-gateway + singular: ingressgateway + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: IngressGateway is the Schema for the ingressgateways 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: IngressGatewaySpec defines the desired state of IngressGateway. + properties: + defaults: + description: Defaults is default configuration for all upstream services + properties: + maxConcurrentRequests: + description: |- + The maximum number of concurrent requests that + will be allowed at a single point in time. Use this to limit HTTP/2 traffic, + since HTTP/2 has many requests per connection. + format: int32 + type: integer + maxConnections: + description: |- + The maximum number of connections a service instance + will be allowed to establish against the given upstream. Use this to limit + HTTP/1.1 traffic, since HTTP/1.1 has a request per connection. + format: int32 + type: integer + maxPendingRequests: + description: |- + The maximum number of requests that will be queued + while waiting for a connection to be established. + format: int32 + type: integer + passiveHealthCheck: + description: |- + PassiveHealthCheck configuration determines how upstream proxy instances will + be monitored for removal from the load balancing pool. + properties: + baseEjectionTime: + description: |- + The base time that a host is ejected for. The real time is equal to the base time + multiplied by the number of times the host has been ejected and is capped by + max_ejection_time (Default 300s). Defaults to 30s. + type: string + enforcingConsecutive5xx: + description: |- + EnforcingConsecutive5xx is the % chance that a host will be actually ejected + when an outlier status is detected through consecutive 5xx. + This setting can be used to disable ejection or to ramp it up slowly. + Ex. Setting this to 10 will make it a 10% chance that the host will be ejected. + format: int32 + type: integer + interval: + description: |- + Interval between health check analysis sweeps. Each sweep may remove + hosts or return hosts to the pool. Ex. setting this to "10s" will set + the interval to 10 seconds. + type: string + maxEjectionPercent: + description: |- + The maximum % of an upstream cluster that can be ejected due to outlier detection. + Defaults to 10% but will eject at least one host regardless of the value. + format: int32 + type: integer + maxFailures: + description: |- + MaxFailures is the count of consecutive failures that results in a host + being removed from the pool. + format: int32 + type: integer + type: object + type: object + listeners: + description: |- + Listeners declares what ports the ingress gateway should listen on, and + what services to associated to those ports. + items: + description: IngressListener manages the configuration for a listener + on a specific port. + properties: + port: + description: Port declares the port on which the ingress gateway + should listen for traffic. + type: integer + protocol: + description: |- + Protocol declares what type of traffic this listener is expected to + receive. Depending on the protocol, a listener might support multiplexing + services over a single port, or additional discovery chain features. The + current supported values are: (tcp | http | http2 | grpc). + type: string + services: + description: |- + Services declares the set of services to which the listener forwards + traffic. + For "tcp" protocol listeners, only a single service is allowed. + For "http" listeners, multiple services can be declared. + items: + description: |- + IngressService manages configuration for services that are exposed to + ingress traffic. + properties: + hosts: + description: |- + Hosts is a list of hostnames which should be associated to this service on + the defined listener. Only allowed on layer 7 protocols, this will be used + to route traffic to the service by matching the Host header of the HTTP + request. + + + If a host is provided for a service that also has a wildcard specifier + defined, the host will override the wildcard-specifier-provided + ".*" domain for that listener. + + + This cannot be specified when using the wildcard specifier, "*", or when + using a "tcp" listener. + items: + type: string + type: array + maxConcurrentRequests: + description: |- + The maximum number of concurrent requests that + will be allowed at a single point in time. Use this to limit HTTP/2 traffic, + since HTTP/2 has many requests per connection. + format: int32 + type: integer + maxConnections: + description: |- + The maximum number of connections a service instance + will be allowed to establish against the given upstream. Use this to limit + HTTP/1.1 traffic, since HTTP/1.1 has a request per connection. + format: int32 + type: integer + maxPendingRequests: + description: |- + The maximum number of requests that will be queued + while waiting for a connection to be established. + format: int32 + type: integer + name: + description: |- + Name declares the service to which traffic should be forwarded. + + + This can either be a specific service, or the wildcard specifier, + "*". If the wildcard specifier is provided, the listener must be of "http" + protocol and means that the listener will forward traffic to all services. + + + A name can be specified on multiple listeners, and will be exposed on both + of the listeners. + type: string + namespace: + description: |- + Namespace is the namespace where the service is located. + Namespacing is a Consul Enterprise feature. + type: string + partition: + description: |- + Partition is the admin-partition where the service is located. + Partitioning is a Consul Enterprise feature. + type: string + passiveHealthCheck: + description: |- + PassiveHealthCheck configuration determines how upstream proxy instances will + be monitored for removal from the load balancing pool. + properties: + baseEjectionTime: + description: |- + The base time that a host is ejected for. The real time is equal to the base time + multiplied by the number of times the host has been ejected and is capped by + max_ejection_time (Default 300s). Defaults to 30s. + type: string + enforcingConsecutive5xx: + description: |- + EnforcingConsecutive5xx is the % chance that a host will be actually ejected + when an outlier status is detected through consecutive 5xx. + This setting can be used to disable ejection or to ramp it up slowly. + Ex. Setting this to 10 will make it a 10% chance that the host will be ejected. + format: int32 + type: integer + interval: + description: |- + Interval between health check analysis sweeps. Each sweep may remove + hosts or return hosts to the pool. Ex. setting this to "10s" will set + the interval to 10 seconds. + type: string + maxEjectionPercent: + description: |- + The maximum % of an upstream cluster that can be ejected due to outlier detection. + Defaults to 10% but will eject at least one host regardless of the value. + format: int32 + type: integer + maxFailures: + description: |- + MaxFailures is the count of consecutive failures that results in a host + being removed from the pool. + format: int32 + type: integer + type: object + requestHeaders: + description: Allow HTTP header manipulation to be configured. + properties: + add: + additionalProperties: + type: string + description: |- + Add is a set of name -> value pairs that should be appended to the request + or response (i.e. allowing duplicates if the same header already exists). + type: object + remove: + description: |- + Remove is the set of header names that should be stripped from the request + or response. + items: + type: string + type: array + set: + additionalProperties: + type: string + description: |- + Set is a set of name -> value pairs that should be added to the request or + response, overwriting any existing header values of the same name. + type: object + type: object + responseHeaders: + description: |- + HTTPHeaderModifiers is a set of rules for HTTP header modification that + should be performed by proxies as the request passes through them. It can + operate on either request or response headers depending on the context in + which it is used. + properties: + add: + additionalProperties: + type: string + description: |- + Add is a set of name -> value pairs that should be appended to the request + or response (i.e. allowing duplicates if the same header already exists). + type: object + remove: + description: |- + Remove is the set of header names that should be stripped from the request + or response. + items: + type: string + type: array + set: + additionalProperties: + type: string + description: |- + Set is a set of name -> value pairs that should be added to the request or + response, overwriting any existing header values of the same name. + type: object + type: object + tls: + description: TLS allows specifying some TLS configuration + per listener. + properties: + sds: + description: SDS allows configuring TLS certificate + from an SDS service. + properties: + certResource: + description: CertResource is the SDS resource + name to request when fetching the certificate + from the SDS service. + type: string + clusterName: + description: |- + ClusterName is the SDS cluster name to connect to, to retrieve certificates. + This cluster must be specified in the Gateway's bootstrap configuration. + type: string + type: object + type: object + type: object + type: array + tls: + description: TLS config for this listener. + properties: + cipherSuites: + description: |- + Define a subset of cipher suites to restrict + Only applicable to connections negotiated via TLS 1.2 or earlier. + items: + type: string + type: array + enabled: + description: Indicates that TLS should be enabled for this + gateway service. + type: boolean + sds: + description: SDS allows configuring TLS certificate from + an SDS service. + properties: + certResource: + description: CertResource is the SDS resource name to + request when fetching the certificate from the SDS + service. + type: string + clusterName: + description: |- + ClusterName is the SDS cluster name to connect to, to retrieve certificates. + This cluster must be specified in the Gateway's bootstrap configuration. + type: string + type: object + tlsMaxVersion: + description: |- + TLSMaxVersion sets the default maximum TLS version supported. Must be greater than or equal to `TLSMinVersion`. + One of `TLS_AUTO`, `TLSv1_0`, `TLSv1_1`, `TLSv1_2`, or `TLSv1_3`. + If unspecified, Envoy will default to TLS 1.3 as a max version for incoming connections. + type: string + tlsMinVersion: + description: |- + TLSMinVersion sets the default minimum TLS version supported. + One of `TLS_AUTO`, `TLSv1_0`, `TLSv1_1`, `TLSv1_2`, or `TLSv1_3`. + If unspecified, Envoy v1.22.0 and newer will default to TLS 1.2 as a min version, + while older releases of Envoy default to TLS 1.0. + type: string + required: + - enabled + type: object + type: object + type: array + tls: + description: TLS holds the TLS configuration for this gateway. + properties: + cipherSuites: + description: |- + Define a subset of cipher suites to restrict + Only applicable to connections negotiated via TLS 1.2 or earlier. + items: + type: string + type: array + enabled: + description: Indicates that TLS should be enabled for this gateway + service. + type: boolean + sds: + description: SDS allows configuring TLS certificate from an SDS + service. + properties: + certResource: + description: CertResource is the SDS resource name to request + when fetching the certificate from the SDS service. + type: string + clusterName: + description: |- + ClusterName is the SDS cluster name to connect to, to retrieve certificates. + This cluster must be specified in the Gateway's bootstrap configuration. + type: string + type: object + tlsMaxVersion: + description: |- + TLSMaxVersion sets the default maximum TLS version supported. Must be greater than or equal to `TLSMinVersion`. + One of `TLS_AUTO`, `TLSv1_0`, `TLSv1_1`, `TLSv1_2`, or `TLSv1_3`. + If unspecified, Envoy will default to TLS 1.3 as a max version for incoming connections. + type: string + tlsMinVersion: + description: |- + TLSMinVersion sets the default minimum TLS version supported. + One of `TLS_AUTO`, `TLSv1_0`, `TLSv1_1`, `TLSv1_2`, or `TLSv1_3`. + If unspecified, Envoy v1.22.0 and newer will default to TLS 1.2 as a min version, + while older releases of Envoy default to TLS 1.0. + type: string + required: + - enabled + type: object + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: |- + Conditions define a readiness condition for a Consul resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-jwtproviders.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-jwtproviders.yaml new file mode 100644 index 0000000000..b52d77b180 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-jwtproviders.yaml @@ -0,0 +1,375 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: jwtproviders.consul.hashicorp.com +spec: + group: consul.hashicorp.com + names: + kind: JWTProvider + listKind: JWTProviderList + plural: jwtproviders + singular: jwtprovider + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: JWTProvider is the Schema for the jwtproviders 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: JWTProviderSpec defines the desired state of JWTProvider + properties: + audiences: + description: |- + Audiences is the set of audiences the JWT is allowed to access. + If specified, all JWTs verified with this provider must address + at least one of these to be considered valid. + items: + type: string + type: array + cacheConfig: + description: |- + CacheConfig defines configuration for caching the validation + result for previously seen JWTs. Caching results can speed up + verification when individual tokens are expected to be handled + multiple times. + properties: + size: + description: |- + Size specifies the maximum number of JWT verification + results to cache. + + + Defaults to 0, meaning that JWT caching is disabled. + type: integer + type: object + clockSkewSeconds: + description: |- + ClockSkewSeconds specifies the maximum allowable time difference + from clock skew when validating the "exp" (Expiration) and "nbf" + (Not Before) claims. + + + Default value is 30 seconds. + type: integer + forwarding: + description: Forwarding defines rules for forwarding verified JWTs + to the backend. + properties: + headerName: + description: |- + HeaderName is a header name to use when forwarding a verified + JWT to the backend. The verified JWT could have been extracted + from any location (query param, header, or cookie). + + + The header value will be base64-URL-encoded, and will not be + padded unless PadForwardPayloadHeader is true. + type: string + padForwardPayloadHeader: + description: |- + PadForwardPayloadHeader determines whether padding should be added + to the base64 encoded token forwarded with ForwardPayloadHeader. + + + Default value is false. + type: boolean + type: object + issuer: + description: |- + Issuer is the entity that must have issued the JWT. + This value must match the "iss" claim of the token. + type: string + jsonWebKeySet: + description: |- + JSONWebKeySet defines a JSON Web Key Set, its location on disk, or the + means with which to fetch a key set from a remote server. + properties: + local: + description: Local specifies a local source for the key set. + properties: + filename: + description: |- + Filename configures a location on disk where the JWKS can be + found. If specified, the file must be present on the disk of ALL + proxies with intentions referencing this provider. + type: string + jwks: + description: JWKS contains a base64 encoded JWKS. + type: string + type: object + remote: + description: Remote specifies how to fetch a key set from a remote + server. + properties: + cacheDuration: + description: |- + CacheDuration is the duration after which cached keys + should be expired. + + + Default value is 5 minutes. + type: string + fetchAsynchronously: + description: |- + FetchAsynchronously indicates that the JWKS should be fetched + when a client request arrives. Client requests will be paused + until the JWKS is fetched. + If false, the proxy listener will wait for the JWKS to be + fetched before being activated. + + + Default value is false. + type: boolean + jwksCluster: + description: JWKSCluster defines how the specified Remote + JWKS URI is to be fetched. + properties: + connectTimeout: + description: |- + The timeout for new network connections to hosts in the cluster. + If not set, a default value of 5s will be used. + type: string + discoveryType: + description: |- + DiscoveryType refers to the service discovery type to use for resolving the cluster. + + + This defaults to STRICT_DNS. + Other options include STATIC, LOGICAL_DNS, EDS or ORIGINAL_DST. + type: string + tlsCertificates: + description: |- + TLSCertificates refers to the data containing certificate authority certificates to use + in verifying a presented peer certificate. + If not specified and a peer certificate is presented it will not be verified. + + + Must be either CaCertificateProviderInstance or TrustedCA. + properties: + caCertificateProviderInstance: + description: CaCertificateProviderInstance Certificate + provider instance for fetching TLS certificates. + properties: + certificateName: + description: |- + CertificateName is used to specify certificate instances or types. For example, "ROOTCA" to specify + a root-certificate (validation context) or "example.com" to specify a certificate for a + particular domain. + + + The default value is the empty string. + type: string + instanceName: + description: |- + InstanceName refers to the certificate provider instance name. + + + The default value is "default". + type: string + type: object + trustedCA: + description: |- + TrustedCA defines TLS certificate data containing certificate authority certificates + to use in verifying a presented peer certificate. + + + Exactly one of Filename, EnvironmentVariable, InlineString or InlineBytes must be specified. + properties: + environmentVariable: + type: string + filename: + type: string + inlineBytes: + format: byte + type: string + inlineString: + type: string + type: object + type: object + type: object + requestTimeoutMs: + description: |- + RequestTimeoutMs is the number of milliseconds to + time out when making a request for the JWKS. + type: integer + retryPolicy: + description: |- + RetryPolicy defines a retry policy for fetching JWKS. + + + There is no retry by default. + properties: + numRetries: + description: |- + NumRetries is the number of times to retry fetching the JWKS. + The retry strategy uses jittered exponential backoff with + a base interval of 1s and max of 10s. + + + Default value is 0. + type: integer + retryPolicyBackOff: + description: |- + Retry's backoff policy. + + + Defaults to Envoy's backoff policy. + properties: + baseInterval: + description: |- + BaseInterval to be used for the next back off computation. + + + The default value from envoy is 1s. + type: string + maxInterval: + description: |- + MaxInternal to be used to specify the maximum interval between retries. + Optional but should be greater or equal to BaseInterval. + + + Defaults to 10 times BaseInterval. + type: string + type: object + type: object + uri: + description: URI is the URI of the server to query for the + JWKS. + type: string + type: object + type: object + locations: + description: |- + Locations where the JWT will be present in requests. + Envoy will check all of these locations to extract a JWT. + If no locations are specified Envoy will default to: + 1. Authorization header with Bearer schema: + "Authorization: Bearer " + 2. accessToken query parameter. + items: + description: |- + JWTLocation is a location where the JWT could be present in requests. + + + Only one of Header, QueryParam, or Cookie can be specified. + properties: + cookie: + description: Cookie defines how to extract a JWT from an HTTP + request cookie. + properties: + name: + description: Name is the name of the cookie containing the + token. + type: string + type: object + header: + description: Header defines how to extract a JWT from an HTTP + request header. + properties: + forward: + description: |- + Forward defines whether the header with the JWT should be + forwarded after the token has been verified. If false, the + header will not be forwarded to the backend. + + + Default value is false. + type: boolean + name: + description: Name is the name of the header containing the + token. + type: string + valuePrefix: + description: |- + ValuePrefix is an optional prefix that precedes the token in the + header value. + For example, "Bearer " is a standard value prefix for a header named + "Authorization", but the prefix is not part of the token itself: + "Authorization: Bearer " + type: string + type: object + queryParam: + description: |- + QueryParam defines how to extract a JWT from an HTTP request + query parameter. + properties: + name: + description: Name is the name of the query param containing + the token. + type: string + type: object + type: object + type: array + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: |- + Conditions define a readiness condition for a Consul resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-meshconfigurations.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-meshconfigurations.yaml new file mode 100644 index 0000000000..36d644d381 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-meshconfigurations.yaml @@ -0,0 +1,107 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: meshconfigurations.mesh.consul.hashicorp.com +spec: + group: mesh.consul.hashicorp.com + names: + kind: MeshConfiguration + listKind: MeshConfigurationList + plural: meshconfigurations + singular: meshconfiguration + scope: Cluster + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v2beta1 + schema: + openAPIV3Schema: + description: MeshConfiguration is the Schema for the Mesh Configuration + 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: |- + MeshConfiguration is responsible for configuring the default behavior of Mesh Gateways. + This is a Resource type. + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: |- + Conditions define a readiness condition for a Consul resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-meshes.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-meshes.yaml new file mode 100644 index 0000000000..f81e61a2c5 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-meshes.yaml @@ -0,0 +1,214 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: meshes.consul.hashicorp.com +spec: + group: consul.hashicorp.com + names: + kind: Mesh + listKind: MeshList + plural: meshes + singular: mesh + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: Mesh is the Schema for the mesh 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: MeshSpec defines the desired state of Mesh. + properties: + allowEnablingPermissiveMutualTLS: + description: |- + AllowEnablingPermissiveMutualTLS must be true in order to allow setting + MutualTLSMode=permissive in either service-defaults or proxy-defaults. + type: boolean + http: + description: HTTP defines the HTTP configuration for the service mesh. + properties: + sanitizeXForwardedClientCert: + type: boolean + required: + - sanitizeXForwardedClientCert + type: object + peering: + description: Peering defines the peering configuration for the service + mesh. + properties: + peerThroughMeshGateways: + description: |- + PeerThroughMeshGateways determines whether peering traffic between + control planes should flow through mesh gateways. If enabled, + Consul servers will advertise mesh gateway addresses as their own. + Additionally, mesh gateways will configure themselves to expose + the local servers using a peering-specific SNI. + type: boolean + type: object + tls: + description: TLS defines the TLS configuration for the service mesh. + properties: + incoming: + description: |- + Incoming defines the TLS configuration for inbound mTLS connections targeting + the public listener on Connect and TerminatingGateway proxy kinds. + properties: + cipherSuites: + description: |- + CipherSuites sets the default list of TLS cipher suites to support when negotiating connections using TLS 1.2 or earlier. + If unspecified, Envoy will use a default server cipher list. The list of supported cipher suites can be seen in + https://github.com/hashicorp/consul/blob/v1.11.2/types/tls.go#L154-L169 and is dependent on underlying support in Envoy. + Future releases of Envoy may remove currently-supported but insecure cipher suites, + and future releases of Consul may add new supported cipher suites if any are added to Envoy. + items: + type: string + type: array + tlsMaxVersion: + description: |- + TLSMaxVersion sets the default maximum TLS version supported. Must be greater than or equal to `TLSMinVersion`. + One of `TLS_AUTO`, `TLSv1_0`, `TLSv1_1`, `TLSv1_2`, or `TLSv1_3`. + If unspecified, Envoy will default to TLS 1.3 as a max version for incoming connections. + type: string + tlsMinVersion: + description: |- + TLSMinVersion sets the default minimum TLS version supported. + One of `TLS_AUTO`, `TLSv1_0`, `TLSv1_1`, `TLSv1_2`, or `TLSv1_3`. + If unspecified, Envoy v1.22.0 and newer will default to TLS 1.2 as a min version, + while older releases of Envoy default to TLS 1.0. + type: string + type: object + outgoing: + description: |- + Outgoing defines the TLS configuration for outbound mTLS connections dialing upstreams + from Connect and IngressGateway proxy kinds. + properties: + cipherSuites: + description: |- + CipherSuites sets the default list of TLS cipher suites to support when negotiating connections using TLS 1.2 or earlier. + If unspecified, Envoy will use a default server cipher list. The list of supported cipher suites can be seen in + https://github.com/hashicorp/consul/blob/v1.11.2/types/tls.go#L154-L169 and is dependent on underlying support in Envoy. + Future releases of Envoy may remove currently-supported but insecure cipher suites, + and future releases of Consul may add new supported cipher suites if any are added to Envoy. + items: + type: string + type: array + tlsMaxVersion: + description: |- + TLSMaxVersion sets the default maximum TLS version supported. Must be greater than or equal to `TLSMinVersion`. + One of `TLS_AUTO`, `TLSv1_0`, `TLSv1_1`, `TLSv1_2`, or `TLSv1_3`. + If unspecified, Envoy will default to TLS 1.3 as a max version for incoming connections. + type: string + tlsMinVersion: + description: |- + TLSMinVersion sets the default minimum TLS version supported. + One of `TLS_AUTO`, `TLSv1_0`, `TLSv1_1`, `TLSv1_2`, or `TLSv1_3`. + If unspecified, Envoy v1.22.0 and newer will default to TLS 1.2 as a min version, + while older releases of Envoy default to TLS 1.0. + type: string + type: object + type: object + transparentProxy: + description: TransparentProxy controls the configuration specific + to proxies in "transparent" mode. Added in v1.10.0. + properties: + meshDestinationsOnly: + description: |- + MeshDestinationsOnly determines whether sidecar proxies operating in "transparent" mode can proxy traffic + to IP addresses not registered in Consul's catalog. If enabled, traffic will only be proxied to upstreams + with service registrations in the catalog. + type: boolean + type: object + validateClusters: + description: |- + ValidateClusters controls whether the clusters the route table refers to are validated. The default value is + false. When set to false and a route refers to a cluster that does not exist, the route table loads and routing + to a non-existent cluster results in a 404. When set to true and the route is set to a cluster that do not exist, + the route table will not load. For more information, refer to + [HTTP route configuration in the Envoy docs](https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route.proto#envoy-v3-api-field-config-route-v3-routeconfiguration-validate-clusters) + for more details. + type: boolean + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: |- + Conditions define a readiness condition for a Consul resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-meshgateways.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-meshgateways.yaml new file mode 100644 index 0000000000..553d0660fe --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-meshgateways.yaml @@ -0,0 +1,140 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: meshgateways.mesh.consul.hashicorp.com +spec: + group: mesh.consul.hashicorp.com + names: + kind: MeshGateway + listKind: MeshGatewayList + plural: meshgateways + singular: meshgateway + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v2beta1 + schema: + openAPIV3Schema: + description: MeshGateway is the Schema for the Mesh Gateway 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: + properties: + gatewayClassName: + description: GatewayClassName is the name of the GatewayClass used + by the MeshGateway + type: string + listeners: + items: + properties: + name: + type: string + port: + format: int32 + maximum: 65535 + minimum: 0 + type: integer + protocol: + enum: + - TCP + type: string + type: object + minItems: 1 + type: array + workloads: + description: Selection of workloads to be configured as mesh gateways + properties: + filter: + type: string + names: + items: + type: string + type: array + prefixes: + items: + type: string + type: array + type: object + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: |- + Conditions define a readiness condition for a Consul resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-meshservices.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-meshservices.yaml new file mode 100644 index 0000000000..1623749f63 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-meshservices.yaml @@ -0,0 +1,61 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: meshservices.consul.hashicorp.com +spec: + group: consul.hashicorp.com + names: + kind: MeshService + listKind: MeshServiceList + plural: meshservices + singular: meshservice + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: MeshService holds a reference to an externally managed Consul + Service Mesh service. + 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 MeshService. + properties: + name: + description: Name holds the service name for a Consul service. + type: string + peer: + description: |- + Peer optionally specifies the name of the peer exporting the Consul service. + If not specified, the Consul service is assumed to be in the local datacenter. + type: string + type: object + type: object + served: true + storage: true +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-peeringacceptors.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-peeringacceptors.yaml new file mode 100644 index 0000000000..60b31d986a --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-peeringacceptors.yaml @@ -0,0 +1,152 @@ +{{- if and .Values.connectInject.enabled .Values.global.peering.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: peeringacceptors.consul.hashicorp.com +spec: + group: consul.hashicorp.com + names: + kind: PeeringAcceptor + listKind: PeeringAcceptorList + plural: peeringacceptors + shortNames: + - peering-acceptor + singular: peeringacceptor + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: PeeringAcceptor is the Schema for the peeringacceptors 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: PeeringAcceptorSpec defines the desired state of PeeringAcceptor. + properties: + peer: + description: Peer describes the information needed to create a peering. + properties: + secret: + description: Secret describes how to store the generated peering + token. + properties: + backend: + description: 'Backend is where the generated secret is stored. + Currently supports the value: "kubernetes".' + type: string + key: + description: Key is the key of the secret generated. + type: string + name: + description: Name is the name of the secret generated. + type: string + type: object + type: object + required: + - peer + type: object + status: + description: PeeringAcceptorStatus defines the observed state of PeeringAcceptor. + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: |- + Conditions define a readiness condition for a Consul resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + latestPeeringVersion: + description: LatestPeeringVersion is the latest version of the resource + that was reconciled. + format: int64 + type: integer + secret: + description: SecretRef shows the status of the secret. + properties: + backend: + description: 'Backend is where the generated secret is stored. + Currently supports the value: "kubernetes".' + type: string + key: + description: Key is the key of the secret generated. + type: string + name: + description: Name is the name of the secret generated. + type: string + resourceVersion: + description: ResourceVersion is the resource version for the secret. + type: string + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-peeringdialers.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-peeringdialers.yaml new file mode 100644 index 0000000000..562c760938 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-peeringdialers.yaml @@ -0,0 +1,152 @@ +{{- if and .Values.connectInject.enabled .Values.global.peering.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: peeringdialers.consul.hashicorp.com +spec: + group: consul.hashicorp.com + names: + kind: PeeringDialer + listKind: PeeringDialerList + plural: peeringdialers + shortNames: + - peering-dialer + singular: peeringdialer + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: PeeringDialer is the Schema for the peeringdialers 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: PeeringDialerSpec defines the desired state of PeeringDialer. + properties: + peer: + description: Peer describes the information needed to create a peering. + properties: + secret: + description: Secret describes how to store the generated peering + token. + properties: + backend: + description: 'Backend is where the generated secret is stored. + Currently supports the value: "kubernetes".' + type: string + key: + description: Key is the key of the secret generated. + type: string + name: + description: Name is the name of the secret generated. + type: string + type: object + type: object + required: + - peer + type: object + status: + description: PeeringDialerStatus defines the observed state of PeeringDialer. + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: |- + Conditions define a readiness condition for a Consul resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + latestPeeringVersion: + description: LatestPeeringVersion is the latest version of the resource + that was reconciled. + format: int64 + type: integer + secret: + description: SecretRef shows the status of the secret. + properties: + backend: + description: 'Backend is where the generated secret is stored. + Currently supports the value: "kubernetes".' + type: string + key: + description: Key is the key of the secret generated. + type: string + name: + description: Name is the name of the secret generated. + type: string + resourceVersion: + description: ResourceVersion is the resource version for the secret. + type: string + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-proxyconfigurations.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-proxyconfigurations.yaml new file mode 100644 index 0000000000..464fdfeaae --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-proxyconfigurations.yaml @@ -0,0 +1,426 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: proxyconfigurations.mesh.consul.hashicorp.com +spec: + group: mesh.consul.hashicorp.com + names: + kind: ProxyConfiguration + listKind: ProxyConfigurationList + plural: proxyconfigurations + shortNames: + - proxy-configuration + singular: proxyconfiguration + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v2beta1 + schema: + openAPIV3Schema: + description: ProxyConfiguration is the Schema for the TCP Routes 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: This is a Resource type. + properties: + bootstrapConfig: + description: |- + bootstrap_config is the configuration that requires proxies + to be restarted to be applied. + properties: + dogstatsdUrl: + type: string + overrideJsonTpl: + type: string + prometheusBindAddr: + type: string + readyBindAddr: + type: string + staticClustersJson: + type: string + staticListenersJson: + type: string + statsBindAddr: + type: string + statsConfigJson: + type: string + statsFlushInterval: + type: string + statsSinksJson: + type: string + statsTags: + items: + type: string + type: array + statsdUrl: + type: string + telemetryCollectorBindSocketDir: + type: string + tracingConfigJson: + type: string + type: object + dynamicConfig: + description: |- + dynamic_config is the configuration that could be changed + dynamically (i.e. without needing restart). + properties: + accessLogs: + description: AccessLogs configures the output and format of Envoy + access logs + properties: + disableListenerLogs: + description: |- + DisableListenerLogs turns off just listener logs for connections rejected by Envoy because they don't + have a matching listener filter. + type: boolean + enabled: + description: Enabled turns off all access logging + type: boolean + jsonFormat: + description: |- + The presence of one format string or the other implies the access log string encoding. + Defining both is invalid. + type: string + path: + description: Path is the output file to write logs + type: string + textFormat: + type: string + type: + description: 'Type selects the output for logs: "file", "stderr". + "stdout"' + enum: + - LOG_SINK_TYPE_DEFAULT + - LOG_SINK_TYPE_FILE + - LOG_SINK_TYPE_STDERR + - LOG_SINK_TYPE_STDOUT + format: int32 + type: string + type: object + exposeConfig: + properties: + exposePaths: + items: + properties: + listenerPort: + format: int32 + type: integer + localPathPort: + format: int32 + type: integer + path: + type: string + protocol: + enum: + - EXPOSE_PATH_PROTOCOL_HTTP + - EXPOSE_PATH_PROTOCOL_HTTP2 + format: int32 + type: string + type: object + type: array + type: object + inboundConnections: + description: inbound_connections configures inbound connections + to the proxy. + properties: + balanceInboundConnections: + enum: + - BALANCE_CONNECTIONS_DEFAULT + - BALANCE_CONNECTIONS_EXACT + format: int32 + type: string + maxInboundConnections: + format: int32 + type: integer + type: object + listenerTracingJson: + type: string + localClusterJson: + type: string + localConnection: + additionalProperties: + description: Referenced by ProxyConfiguration + properties: + connectTimeout: + description: "A Duration represents a signed, fixed-length + span of time represented\nas a count of seconds and fractions + of seconds at nanosecond\nresolution. It is independent + of any calendar and concepts like \"day\"\nor \"month\". + It is related to Timestamp in that the difference between\ntwo + Timestamp values is a Duration and it can be added or + subtracted\nfrom a Timestamp. Range is approximately +-10,000 + years.\n\n\n# Examples\n\n\nExample 1: Compute Duration + from two Timestamps in pseudo code.\n\n\n\tTimestamp start + = ...;\n\tTimestamp end = ...;\n\tDuration duration = + ...;\n\n\n\tduration.seconds = end.seconds - start.seconds;\n\tduration.nanos + = end.nanos - start.nanos;\n\n\n\tif (duration.seconds + < 0 && duration.nanos > 0) {\n\t duration.seconds += + 1;\n\t duration.nanos -= 1000000000;\n\t} else if (duration.seconds + > 0 && duration.nanos < 0) {\n\t duration.seconds -= + 1;\n\t duration.nanos += 1000000000;\n\t}\n\n\nExample + 2: Compute Timestamp from Timestamp + Duration in pseudo + code.\n\n\n\tTimestamp start = ...;\n\tDuration duration + = ...;\n\tTimestamp end = ...;\n\n\n\tend.seconds = start.seconds + + duration.seconds;\n\tend.nanos = start.nanos + duration.nanos;\n\n\n\tif + (end.nanos < 0) {\n\t end.seconds -= 1;\n\t end.nanos + += 1000000000;\n\t} else if (end.nanos >= 1000000000) + {\n\t end.seconds += 1;\n\t end.nanos -= 1000000000;\n\t}\n\n\nExample + 3: Compute Duration from datetime.timedelta in Python.\n\n\n\ttd + = datetime.timedelta(days=3, minutes=10)\n\tduration = + Duration()\n\tduration.FromTimedelta(td)\n\n\n# JSON Mapping\n\n\nIn + JSON format, the Duration type is encoded as a string + rather than an\nobject, where the string ends in the suffix + \"s\" (indicating seconds) and\nis preceded by the number + of seconds, with nanoseconds expressed as\nfractional + seconds. For example, 3 seconds with 0 nanoseconds should + be\nencoded in JSON format as \"3s\", while 3 seconds + and 1 nanosecond should\nbe expressed in JSON format as + \"3.000000001s\", and 3 seconds and 1\nmicrosecond should + be expressed in JSON format as \"3.000001s\"." + format: duration + properties: + nanos: + description: |- + Signed fractions of a second at nanosecond resolution of the span + of time. Durations less than one second are represented with a 0 + `seconds` field and a positive or negative `nanos` field. For durations + of one second or more, a non-zero value for the `nanos` field must be + of the same sign as the `seconds` field. Must be from -999,999,999 + to +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: |- + Signed seconds of the span of time. Must be from -315,576,000,000 + to +315,576,000,000 inclusive. Note: these bounds are computed from: + 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years + format: int64 + type: integer + type: object + requestTimeout: + description: "A Duration represents a signed, fixed-length + span of time represented\nas a count of seconds and fractions + of seconds at nanosecond\nresolution. It is independent + of any calendar and concepts like \"day\"\nor \"month\". + It is related to Timestamp in that the difference between\ntwo + Timestamp values is a Duration and it can be added or + subtracted\nfrom a Timestamp. Range is approximately +-10,000 + years.\n\n\n# Examples\n\n\nExample 1: Compute Duration + from two Timestamps in pseudo code.\n\n\n\tTimestamp start + = ...;\n\tTimestamp end = ...;\n\tDuration duration = + ...;\n\n\n\tduration.seconds = end.seconds - start.seconds;\n\tduration.nanos + = end.nanos - start.nanos;\n\n\n\tif (duration.seconds + < 0 && duration.nanos > 0) {\n\t duration.seconds += + 1;\n\t duration.nanos -= 1000000000;\n\t} else if (duration.seconds + > 0 && duration.nanos < 0) {\n\t duration.seconds -= + 1;\n\t duration.nanos += 1000000000;\n\t}\n\n\nExample + 2: Compute Timestamp from Timestamp + Duration in pseudo + code.\n\n\n\tTimestamp start = ...;\n\tDuration duration + = ...;\n\tTimestamp end = ...;\n\n\n\tend.seconds = start.seconds + + duration.seconds;\n\tend.nanos = start.nanos + duration.nanos;\n\n\n\tif + (end.nanos < 0) {\n\t end.seconds -= 1;\n\t end.nanos + += 1000000000;\n\t} else if (end.nanos >= 1000000000) + {\n\t end.seconds += 1;\n\t end.nanos -= 1000000000;\n\t}\n\n\nExample + 3: Compute Duration from datetime.timedelta in Python.\n\n\n\ttd + = datetime.timedelta(days=3, minutes=10)\n\tduration = + Duration()\n\tduration.FromTimedelta(td)\n\n\n# JSON Mapping\n\n\nIn + JSON format, the Duration type is encoded as a string + rather than an\nobject, where the string ends in the suffix + \"s\" (indicating seconds) and\nis preceded by the number + of seconds, with nanoseconds expressed as\nfractional + seconds. For example, 3 seconds with 0 nanoseconds should + be\nencoded in JSON format as \"3s\", while 3 seconds + and 1 nanosecond should\nbe expressed in JSON format as + \"3.000000001s\", and 3 seconds and 1\nmicrosecond should + be expressed in JSON format as \"3.000001s\"." + format: duration + properties: + nanos: + description: |- + Signed fractions of a second at nanosecond resolution of the span + of time. Durations less than one second are represented with a 0 + `seconds` field and a positive or negative `nanos` field. For durations + of one second or more, a non-zero value for the `nanos` field must be + of the same sign as the `seconds` field. Must be from -999,999,999 + to +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: |- + Signed seconds of the span of time. Must be from -315,576,000,000 + to +315,576,000,000 inclusive. Note: these bounds are computed from: + 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years + format: int64 + type: integer + type: object + type: object + description: |- + local_connection is the configuration that should be used + to connect to the local application provided per-port. + The map keys should correspond to port names on the workload. + type: object + localWorkloadAddress: + description: |- + deprecated: + local_workload_address, local_workload_port, and local_workload_socket_path + are deprecated and are only needed for migration of existing resources. + + + Deprecated: Marked as deprecated in pbmesh/v2beta1/proxy_configuration.proto. + type: string + localWorkloadPort: + description: 'Deprecated: Marked as deprecated in pbmesh/v2beta1/proxy_configuration.proto.' + format: int32 + type: integer + localWorkloadSocketPath: + description: 'Deprecated: Marked as deprecated in pbmesh/v2beta1/proxy_configuration.proto.' + type: string + meshGatewayMode: + enum: + - MESH_GATEWAY_MODE_UNSPECIFIED + - MESH_GATEWAY_MODE_NONE + - MESH_GATEWAY_MODE_LOCAL + - MESH_GATEWAY_MODE_REMOTE + format: int32 + type: string + mode: + description: mode indicates the proxy's mode. This will default + to 'transparent'. + enum: + - PROXY_MODE_DEFAULT + - PROXY_MODE_TRANSPARENT + - PROXY_MODE_DIRECT + format: int32 + type: string + mutualTlsMode: + enum: + - MUTUAL_TLS_MODE_DEFAULT + - MUTUAL_TLS_MODE_STRICT + - MUTUAL_TLS_MODE_PERMISSIVE + format: int32 + type: string + publicListenerJson: + type: string + transparentProxy: + properties: + dialedDirectly: + description: |- + dialed_directly indicates whether this proxy should be dialed using original destination IP + in the connection rather than load balance between all endpoints. + type: boolean + outboundListenerPort: + description: |- + outbound_listener_port is the port for the proxy's outbound listener. + This defaults to 15001. + format: int32 + type: integer + type: object + type: object + opaqueConfig: + description: |- + deprecated: prevent usage when using v2 APIs directly. + needed for backwards compatibility + + + Deprecated: Marked as deprecated in pbmesh/v2beta1/proxy_configuration.proto. + type: object + x-kubernetes-preserve-unknown-fields: true + workloads: + description: |- + Selection of workloads this proxy configuration should apply to. + These can be prefixes or specific workload names. + properties: + filter: + type: string + names: + items: + type: string + type: array + prefixes: + items: + type: string + type: array + type: object + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: |- + Conditions define a readiness condition for a Consul resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-proxydefaults.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-proxydefaults.yaml new file mode 100644 index 0000000000..a5fa8178f3 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-proxydefaults.yaml @@ -0,0 +1,278 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: proxydefaults.consul.hashicorp.com +spec: + group: consul.hashicorp.com + names: + kind: ProxyDefaults + listKind: ProxyDefaultsList + plural: proxydefaults + shortNames: + - proxy-defaults + singular: proxydefaults + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: ProxyDefaults is the Schema for the proxydefaults 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: ProxyDefaultsSpec defines the desired state of ProxyDefaults. + properties: + accessLogs: + description: AccessLogs controls all envoy instances' access logging + configuration. + properties: + disableListenerLogs: + description: |- + DisableListenerLogs turns off just listener logs for connections rejected by Envoy because they don't + have a matching listener filter. + type: boolean + enabled: + description: Enabled turns on all access logging + type: boolean + jsonFormat: + description: |- + JSONFormat is a JSON-formatted string of an Envoy access log format dictionary. + See for more info on formatting: https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#format-dictionaries + Defining JSONFormat and TextFormat is invalid. + type: string + path: + description: Path is the output file to write logs for file-type + logging + type: string + textFormat: + description: |- + TextFormat is a representation of Envoy access logs format. + See for more info on formatting: https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#format-strings + Defining JSONFormat and TextFormat is invalid. + type: string + type: + description: |- + Type selects the output for logs + one of "file", "stderr". "stdout" + type: string + type: object + config: + description: |- + Config is an arbitrary map of configuration values used by Connect proxies. + Any values that your proxy allows can be configured globally here. + Supports JSON config values. See https://www.consul.io/docs/connect/proxies/envoy#configuration-formatting + type: object + x-kubernetes-preserve-unknown-fields: true + envoyExtensions: + description: EnvoyExtensions are a list of extensions to modify Envoy + proxy configuration. + items: + description: EnvoyExtension has configuration for an extension that + patches Envoy resources. + properties: + arguments: + type: object + x-kubernetes-preserve-unknown-fields: true + name: + type: string + required: + type: boolean + type: object + type: array + expose: + description: Expose controls the default expose path configuration + for Envoy. + properties: + checks: + description: |- + Checks defines whether paths associated with Consul checks will be exposed. + This flag triggers exposing all HTTP and GRPC check paths registered for the service. + type: boolean + paths: + description: Paths is the list of paths exposed through the proxy. + items: + properties: + listenerPort: + description: ListenerPort defines the port of the proxy's + listener for exposed paths. + type: integer + localPathPort: + description: LocalPathPort is the port that the service + is listening on for the given path. + type: integer + path: + description: Path is the path to expose through the proxy, + ie. "/metrics". + type: string + protocol: + description: |- + Protocol describes the upstream's service protocol. + Valid values are "http" and "http2", defaults to "http". + type: string + type: object + type: array + type: object + failoverPolicy: + description: FailoverPolicy specifies the exact mechanism used for + failover. + properties: + mode: + description: |- + Mode specifies the type of failover that will be performed. Valid values are + "sequential", "" (equivalent to "sequential") and "order-by-locality". + type: string + regions: + description: |- + Regions is the ordered list of the regions of the failover targets. + Valid values can be "us-west-1", "us-west-2", and so on. + items: + type: string + type: array + type: object + meshGateway: + description: MeshGateway controls the default mesh gateway configuration + for this service. + properties: + mode: + description: |- + Mode is the mode that should be used for the upstream connection. + One of none, local, or remote. + type: string + type: object + mode: + description: |- + Mode can be one of "direct" or "transparent". "transparent" represents that inbound and outbound + application traffic is being captured and redirected through the proxy. This mode does not + enable the traffic redirection itself. Instead it signals Consul to configure Envoy as if + traffic is already being redirected. "direct" represents that the proxy's listeners must be + dialed directly by the local application and other proxies. + Note: This cannot be set using the CRD and should be set using annotations on the + services that are part of the mesh. + type: string + mutualTLSMode: + description: |- + MutualTLSMode controls whether mutual TLS is required for all incoming + connections when transparent proxy is enabled. This can be set to + "permissive" or "strict". "strict" is the default which requires mutual + TLS for incoming connections. In the insecure "permissive" mode, + connections to the sidecar proxy public listener port require mutual + TLS, but connections to the service port do not require mutual TLS and + are proxied to the application unmodified. Note: Intentions are not + enforced for non-mTLS connections. To keep your services secure, we + recommend using "strict" mode whenever possible and enabling + "permissive" mode only when necessary. + type: string + prioritizeByLocality: + description: |- + PrioritizeByLocality controls whether the locality of services within the + local partition will be used to prioritize connectivity. + properties: + mode: + description: |- + Mode specifies the type of prioritization that will be performed + when selecting nodes in the local partition. + Valid values are: "" (default "none"), "none", and "failover". + type: string + type: object + transparentProxy: + description: |- + TransparentProxy controls configuration specific to proxies in transparent mode. + Note: This cannot be set using the CRD and should be set using annotations on the + services that are part of the mesh. + properties: + dialedDirectly: + description: |- + DialedDirectly indicates whether transparent proxies can dial this proxy instance directly. + The discovery chain is not considered when dialing a service instance directly. + This setting is useful when addressing stateful services, such as a database cluster with a leader node. + type: boolean + outboundListenerPort: + description: |- + OutboundListenerPort is the port of the listener where outbound application + traffic is being redirected to. + type: integer + type: object + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: |- + Conditions define a readiness condition for a Consul resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-referencegrants-external.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-referencegrants-external.yaml new file mode 100644 index 0000000000..db9cf12027 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-referencegrants-external.yaml @@ -0,0 +1,208 @@ +{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 + gateway.networking.k8s.io/bundle-version: v0.6.2 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + 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 + 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. \n 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. \n All cross-namespace references in Gateway API (with the exception of cross-namespace Gateway-route attachment) require a ReferenceGrant. \n 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. \n Support: Core" + 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. \n 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. \n 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. \n When used to permit a SecretObjectReference: \n * Gateway \n When used to permit a BackendObjectReference: \n * 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. \n 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. \n 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. \n 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: \n * 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: {} + - 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. \n 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. \n All cross-namespace references in Gateway API (with the exception of cross-namespace Gateway-route attachment) require a ReferenceGrant. \n 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. \n Support: Core" + 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. \n 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. \n 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. \n When used to permit a SecretObjectReference: \n * Gateway \n When used to permit a BackendObjectReference: \n * 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. \n 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. \n 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. \n 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: \n * 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: false + subresources: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-registrations.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-registrations.yaml new file mode 100644 index 0000000000..f126978e53 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-registrations.yaml @@ -0,0 +1,257 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: registrations.consul.hashicorp.com +spec: + group: consul.hashicorp.com + names: + kind: Registration + listKind: RegistrationList + plural: registrations + singular: registration + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Registration defines the resource for working with service registrations. + 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 Registration. + properties: + address: + type: string + check: + description: HealthCheck is used to represent a single check. + properties: + checkId: + type: string + definition: + description: |- + HealthCheckDefinition is used to store the details about + a health check's execution. + properties: + body: + type: string + deregisterCriticalServiceAfterDuration: + type: string + grpc: + type: string + grpcUseTLS: + type: boolean + header: + additionalProperties: + items: + type: string + type: array + type: object + http: + type: string + intervalDuration: + type: string + method: + type: string + osService: + type: string + tcp: + type: string + tcpUseTLS: + type: boolean + timeoutDuration: + type: string + tlsServerName: + type: string + tlsSkipVerify: + type: boolean + udp: + type: string + required: + - intervalDuration + type: object + exposedPort: + type: integer + name: + type: string + namespace: + type: string + node: + type: string + notes: + type: string + output: + type: string + partition: + type: string + serviceId: + type: string + serviceName: + type: string + status: + type: string + type: + type: string + required: + - checkId + - definition + - name + - serviceId + - serviceName + - status + type: object + datacenter: + type: string + id: + type: string + locality: + properties: + region: + type: string + zone: + type: string + type: object + node: + type: string + nodeMeta: + additionalProperties: + type: string + type: object + partition: + type: string + service: + properties: + address: + type: string + enableTagOverride: + type: boolean + id: + type: string + locality: + properties: + region: + type: string + zone: + type: string + type: object + meta: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + partition: + type: string + port: + type: integer + socketPath: + type: string + taggedAddresses: + additionalProperties: + properties: + address: + type: string + port: + type: integer + required: + - address + - port + type: object + type: object + tags: + items: + type: string + type: array + weights: + properties: + passing: + type: integer + warning: + type: integer + required: + - passing + - warning + type: object + required: + - name + - port + type: object + skipNodeUpdate: + type: boolean + taggedAddresses: + additionalProperties: + type: string + type: object + type: object + status: + description: RegistrationStatus defines the observed state of Registration. + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: |- + Conditions define a readiness condition for a Consul resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-routeauthfilters.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-routeauthfilters.yaml new file mode 100644 index 0000000000..65403e657a --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-routeauthfilters.yaml @@ -0,0 +1,215 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: routeauthfilters.consul.hashicorp.com +spec: + group: consul.hashicorp.com + names: + kind: RouteAuthFilter + listKind: RouteAuthFilterList + plural: routeauthfilters + singular: routeauthfilter + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: RouteAuthFilter is the Schema for the routeauthfilters 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: RouteAuthFilterSpec defines the desired state of RouteAuthFilter. + properties: + jwt: + description: This re-uses the JWT requirement type from Gateway Policy + Types. + properties: + providers: + description: Providers is a list of providers to consider when + verifying a JWT. + items: + description: GatewayJWTProvider holds the provider and claim + verification information. + properties: + name: + description: |- + Name is the name of the JWT provider. There MUST be a corresponding + "jwt-provider" config entry with this name. + type: string + verifyClaims: + description: VerifyClaims is a list of additional claims + to verify in a JWT's payload. + items: + description: GatewayJWTClaimVerification holds the actual + claim information to be verified. + properties: + path: + description: Path is the path to the claim in the + token JSON. + items: + type: string + type: array + value: + description: |- + Value is the expected value at the given path: + - If the type at the path is a list then we verify + that this value is contained in the list. + + + - If the type at the path is a string then we verify + that this value matches. + type: string + required: + - path + - value + type: object + type: array + required: + - name + type: object + type: array + required: + - providers + type: object + type: object + status: + description: RouteAuthFilterStatus defines the observed state of the gateway. + properties: + 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: ResolvedRefs + description: |- + Conditions describe the current conditions of the Filter. + + + Known condition types are: + + + * "Accepted" + * "ResolvedRefs" + 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 + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-routeretryfilters.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-routeretryfilters.yaml new file mode 100644 index 0000000000..d26dff9c56 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-routeretryfilters.yaml @@ -0,0 +1,121 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: routeretryfilters.consul.hashicorp.com +spec: + group: consul.hashicorp.com + names: + kind: RouteRetryFilter + listKind: RouteRetryFilterList + plural: routeretryfilters + singular: routeretryfilter + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: RouteRetryFilter is the Schema for the routeretryfilters 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: RouteRetryFilterSpec defines the desired state of RouteRetryFilter. + properties: + numRetries: + format: int32 + minimum: 0 + type: integer + retryOn: + items: + type: string + type: array + retryOnConnectFailure: + type: boolean + retryOnStatusCodes: + items: + format: int32 + type: integer + type: array + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: |- + Conditions define a readiness condition for a Consul resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-routetimeoutfilters.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-routetimeoutfilters.yaml new file mode 100644 index 0000000000..568b025204 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-routetimeoutfilters.yaml @@ -0,0 +1,113 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: routetimeoutfilters.consul.hashicorp.com +spec: + group: consul.hashicorp.com + names: + kind: RouteTimeoutFilter + listKind: RouteTimeoutFilterList + plural: routetimeoutfilters + singular: routetimeoutfilter + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: RouteTimeoutFilter is the Schema for the httproutetimeoutfilters + 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: RouteTimeoutFilterSpec defines the desired state of RouteTimeoutFilter. + properties: + idleTimeout: + format: duration + type: string + requestTimeout: + format: duration + type: string + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: |- + Conditions define a readiness condition for a Consul resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-samenessgroups.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-samenessgroups.yaml new file mode 100644 index 0000000000..29cd24f361 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-samenessgroups.yaml @@ -0,0 +1,133 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: samenessgroups.consul.hashicorp.com +spec: + group: consul.hashicorp.com + names: + kind: SamenessGroup + listKind: SamenessGroupList + plural: samenessgroups + shortNames: + - sameness-group + singular: samenessgroup + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: SamenessGroup is the Schema for the samenessgroups 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: SamenessGroupSpec defines the desired state of SamenessGroup. + properties: + defaultForFailover: + description: |- + DefaultForFailover indicates that upstream requests to members of the given sameness group will implicitly failover between members of this sameness group. + When DefaultForFailover is true, the local partition must be a member of the sameness group or IncludeLocal must be set to true. + type: boolean + includeLocal: + description: |- + IncludeLocal is used to include the local partition as the first member of the sameness group. + The local partition can only be a member of a single sameness group. + type: boolean + members: + description: |- + Members are the partitions and peers that are part of the sameness group. + If a member of a sameness group does not exist, it will be ignored. + items: + properties: + partition: + description: |- + The partitions and peers that are part of the sameness group. + A sameness group member cannot define both peer and partition at the same time. + type: string + peer: + type: string + type: object + type: array + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: |- + Conditions define a readiness condition for a Consul resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-servicedefaults.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-servicedefaults.yaml new file mode 100644 index 0000000000..a976d0989b --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-servicedefaults.yaml @@ -0,0 +1,580 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: servicedefaults.consul.hashicorp.com +spec: + group: consul.hashicorp.com + names: + kind: ServiceDefaults + listKind: ServiceDefaultsList + plural: servicedefaults + shortNames: + - service-defaults + singular: servicedefaults + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: ServiceDefaults is the Schema for the servicedefaults 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: ServiceDefaultsSpec defines the desired state of ServiceDefaults. + properties: + balanceInboundConnections: + description: |- + BalanceInboundConnections sets the strategy for allocating inbound connections to the service across + proxy threads. The only supported value is exact_balance. By default, no connection balancing is used. + Refer to the Envoy Connection Balance config for details. + type: string + destination: + description: |- + Destination is an address(es)/port combination that represents an endpoint + outside the mesh. This is only valid when the mesh is configured in "transparent" + mode. Destinations live outside of Consul's catalog, and because of this, they + do not require an artificial node to be created. + properties: + addresses: + description: |- + Addresses is a list of IPs and/or hostnames that can be dialed + and routed through a terminating gateway. + items: + type: string + type: array + port: + description: |- + Port is the port that can be dialed on any of the addresses in this + Destination. + format: int32 + type: integer + type: object + envoyExtensions: + description: EnvoyExtensions are a list of extensions to modify Envoy + proxy configuration. + items: + description: EnvoyExtension has configuration for an extension that + patches Envoy resources. + properties: + arguments: + type: object + x-kubernetes-preserve-unknown-fields: true + name: + type: string + required: + type: boolean + type: object + type: array + expose: + description: Expose controls the default expose path configuration + for Envoy. + properties: + checks: + description: |- + Checks defines whether paths associated with Consul checks will be exposed. + This flag triggers exposing all HTTP and GRPC check paths registered for the service. + type: boolean + paths: + description: Paths is the list of paths exposed through the proxy. + items: + properties: + listenerPort: + description: ListenerPort defines the port of the proxy's + listener for exposed paths. + type: integer + localPathPort: + description: LocalPathPort is the port that the service + is listening on for the given path. + type: integer + path: + description: Path is the path to expose through the proxy, + ie. "/metrics". + type: string + protocol: + description: |- + Protocol describes the upstream's service protocol. + Valid values are "http" and "http2", defaults to "http". + type: string + type: object + type: array + type: object + externalSNI: + description: |- + ExternalSNI is an optional setting that allows for the TLS SNI value + to be changed to a non-connect value when federating with an external system. + type: string + localConnectTimeoutMs: + description: |- + LocalConnectTimeoutMs is the number of milliseconds allowed to make connections to the local application + instance before timing out. Defaults to 5000. + type: integer + localRequestTimeoutMs: + description: |- + LocalRequestTimeoutMs is the timeout for HTTP requests to the local application instance in milliseconds. + Applies to HTTP-based protocols only. If not specified, inherits the Envoy default for + route timeouts (15s). + type: integer + maxInboundConnections: + description: |- + MaxInboundConnections is the maximum number of concurrent inbound connections to + each service instance. Defaults to 0 (using consul's default) if not set. + type: integer + meshGateway: + description: MeshGateway controls the default mesh gateway configuration + for this service. + properties: + mode: + description: |- + Mode is the mode that should be used for the upstream connection. + One of none, local, or remote. + type: string + type: object + mode: + description: |- + Mode can be one of "direct" or "transparent". "transparent" represents that inbound and outbound + application traffic is being captured and redirected through the proxy. This mode does not + enable the traffic redirection itself. Instead it signals Consul to configure Envoy as if + traffic is already being redirected. "direct" represents that the proxy's listeners must be + dialed directly by the local application and other proxies. + Note: This cannot be set using the CRD and should be set using annotations on the + services that are part of the mesh. + type: string + mutualTLSMode: + description: |- + MutualTLSMode controls whether mutual TLS is required for all incoming + connections when transparent proxy is enabled. This can be set to + "permissive" or "strict". "strict" is the default which requires mutual + TLS for incoming connections. In the insecure "permissive" mode, + connections to the sidecar proxy public listener port require mutual + TLS, but connections to the service port do not require mutual TLS and + are proxied to the application unmodified. Note: Intentions are not + enforced for non-mTLS connections. To keep your services secure, we + recommend using "strict" mode whenever possible and enabling + "permissive" mode only when necessary. + type: string + protocol: + description: |- + Protocol sets the protocol of the service. This is used by Connect proxies for + things like observability features and to unlock usage of the + service-splitter and service-router config entries for a service. + type: string + rateLimits: + description: |- + RateLimits is rate limiting configuration that is applied to + inbound traffic for a service. Rate limiting is a Consul enterprise feature. + properties: + instanceLevel: + description: |- + InstanceLevel represents rate limit configuration + that is applied per service instance. + properties: + requestsMaxBurst: + description: |- + RequestsMaxBurst is the maximum number of requests that can be sent + in a burst. Should be equal to or greater than RequestsPerSecond. + If unset, defaults to RequestsPerSecond. + + + Internally, this is the maximum size of the token bucket used for rate limiting. + type: integer + requestsPerSecond: + description: |- + RequestsPerSecond is the average number of requests per second that can be + made without being throttled. This field is required if RequestsMaxBurst + is set. The allowed number of requests may exceed RequestsPerSecond up to + the value specified in RequestsMaxBurst. + + + Internally, this is the refill rate of the token bucket used for rate limiting. + type: integer + routes: + description: |- + Routes is a list of rate limits applied to specific routes. + For a given request, the first matching route will be applied, if any. + Overrides any top-level configuration. + items: + properties: + pathExact: + description: Exact path to match. Exactly one of PathExact, + PathPrefix, or PathRegex must be specified. + type: string + pathPrefix: + description: Prefix to match. Exactly one of PathExact, + PathPrefix, or PathRegex must be specified. + type: string + pathRegex: + description: Regex to match. Exactly one of PathExact, + PathPrefix, or PathRegex must be specified. + type: string + requestsMaxBurst: + description: |- + RequestsMaxBurst is the maximum number of requests that can be sent + in a burst. Should be equal to or greater than RequestsPerSecond. If unset, + defaults to RequestsPerSecond. Internally, this is the maximum size of the token + bucket used for rate limiting. + type: integer + requestsPerSecond: + description: |- + RequestsPerSecond is the average number of requests per + second that can be made without being throttled. This field is required + if RequestsMaxBurst is set. The allowed number of requests may exceed + RequestsPerSecond up to the value specified in RequestsMaxBurst. + Internally, this is the refill rate of the token bucket used for rate limiting. + type: integer + type: object + type: array + type: object + type: object + transparentProxy: + description: |- + TransparentProxy controls configuration specific to proxies in transparent mode. + Note: This cannot be set using the CRD and should be set using annotations on the + services that are part of the mesh. + properties: + dialedDirectly: + description: |- + DialedDirectly indicates whether transparent proxies can dial this proxy instance directly. + The discovery chain is not considered when dialing a service instance directly. + This setting is useful when addressing stateful services, such as a database cluster with a leader node. + type: boolean + outboundListenerPort: + description: |- + OutboundListenerPort is the port of the listener where outbound application + traffic is being redirected to. + type: integer + type: object + upstreamConfig: + description: |- + UpstreamConfig controls default configuration settings that apply across all upstreams, + and per-upstream configuration overrides. Note that per-upstream configuration applies + across all federated datacenters to the pairing of source and upstream destination services. + properties: + defaults: + description: |- + Defaults contains default configuration for all upstreams of a given + service. The name field must be empty. + properties: + connectTimeoutMs: + description: |- + ConnectTimeoutMs is the number of milliseconds to timeout making a new + connection to this upstream. Defaults to 5000 (5 seconds) if not set. + type: integer + envoyClusterJSON: + description: |- + EnvoyClusterJSON is a complete override ("escape hatch") for the upstream's + cluster. The Connect client TLS certificate and context will be injected + overriding any TLS settings present. + Note: This escape hatch is NOT compatible with the discovery chain and + will be ignored if a discovery chain is active. + type: string + envoyListenerJSON: + description: |- + EnvoyListenerJSON is a complete override ("escape hatch") for the upstream's + listener. + Note: This escape hatch is NOT compatible with the discovery chain and + will be ignored if a discovery chain is active. + type: string + limits: + description: |- + Limits are the set of limits that are applied to the proxy for a specific upstream of a + service instance. + properties: + maxConcurrentRequests: + description: |- + MaxConcurrentRequests is the maximum number of in-flight requests that will be allowed + to the upstream cluster at a point in time. This is mostly applicable to HTTP/2 + clusters since all HTTP/1.1 requests are limited by MaxConnections. + type: integer + maxConnections: + description: |- + MaxConnections is the maximum number of connections the local proxy can + make to the upstream service. + type: integer + maxPendingRequests: + description: |- + MaxPendingRequests is the maximum number of requests that will be queued + waiting for an available connection. This is mostly applicable to HTTP/1.1 + clusters since all HTTP/2 requests are streamed over a single + connection. + type: integer + type: object + meshGateway: + description: MeshGatewayConfig controls how Mesh Gateways + are configured and used. + properties: + mode: + description: |- + Mode is the mode that should be used for the upstream connection. + One of none, local, or remote. + type: string + type: object + name: + description: Name is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides + config entry. + type: string + namespace: + description: Namespace is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides + config entry. + type: string + partition: + description: Partition is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides + config entry. + type: string + passiveHealthCheck: + description: |- + PassiveHealthCheck configuration determines how upstream proxy instances will + be monitored for removal from the load balancing pool. + properties: + baseEjectionTime: + description: |- + The base time that a host is ejected for. The real time is equal to the base time + multiplied by the number of times the host has been ejected and is capped by + max_ejection_time (Default 300s). Defaults to 30s. + type: string + enforcingConsecutive5xx: + description: |- + EnforcingConsecutive5xx is the % chance that a host will be actually ejected + when an outlier status is detected through consecutive 5xx. + This setting can be used to disable ejection or to ramp it up slowly. + Ex. Setting this to 10 will make it a 10% chance that the host will be ejected. + format: int32 + type: integer + interval: + description: |- + Interval between health check analysis sweeps. Each sweep may remove + hosts or return hosts to the pool. Ex. setting this to "10s" will set + the interval to 10 seconds. + type: string + maxEjectionPercent: + description: |- + The maximum % of an upstream cluster that can be ejected due to outlier detection. + Defaults to 10% but will eject at least one host regardless of the value. + format: int32 + type: integer + maxFailures: + description: |- + MaxFailures is the count of consecutive failures that results in a host + being removed from the pool. + format: int32 + type: integer + type: object + peer: + description: Peer is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides + config entry. + type: string + protocol: + description: |- + Protocol describes the upstream's service protocol. Valid values are "tcp", + "http" and "grpc". Anything else is treated as tcp. This enables protocol + aware features like per-request metrics and connection pooling, tracing, + routing etc. + type: string + type: object + overrides: + description: |- + Overrides is a slice of per-service configuration. The name field is + required. + items: + properties: + connectTimeoutMs: + description: |- + ConnectTimeoutMs is the number of milliseconds to timeout making a new + connection to this upstream. Defaults to 5000 (5 seconds) if not set. + type: integer + envoyClusterJSON: + description: |- + EnvoyClusterJSON is a complete override ("escape hatch") for the upstream's + cluster. The Connect client TLS certificate and context will be injected + overriding any TLS settings present. + Note: This escape hatch is NOT compatible with the discovery chain and + will be ignored if a discovery chain is active. + type: string + envoyListenerJSON: + description: |- + EnvoyListenerJSON is a complete override ("escape hatch") for the upstream's + listener. + Note: This escape hatch is NOT compatible with the discovery chain and + will be ignored if a discovery chain is active. + type: string + limits: + description: |- + Limits are the set of limits that are applied to the proxy for a specific upstream of a + service instance. + properties: + maxConcurrentRequests: + description: |- + MaxConcurrentRequests is the maximum number of in-flight requests that will be allowed + to the upstream cluster at a point in time. This is mostly applicable to HTTP/2 + clusters since all HTTP/1.1 requests are limited by MaxConnections. + type: integer + maxConnections: + description: |- + MaxConnections is the maximum number of connections the local proxy can + make to the upstream service. + type: integer + maxPendingRequests: + description: |- + MaxPendingRequests is the maximum number of requests that will be queued + waiting for an available connection. This is mostly applicable to HTTP/1.1 + clusters since all HTTP/2 requests are streamed over a single + connection. + type: integer + type: object + meshGateway: + description: MeshGatewayConfig controls how Mesh Gateways + are configured and used. + properties: + mode: + description: |- + Mode is the mode that should be used for the upstream connection. + One of none, local, or remote. + type: string + type: object + name: + description: Name is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides + config entry. + type: string + namespace: + description: Namespace is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides + config entry. + type: string + partition: + description: Partition is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides + config entry. + type: string + passiveHealthCheck: + description: |- + PassiveHealthCheck configuration determines how upstream proxy instances will + be monitored for removal from the load balancing pool. + properties: + baseEjectionTime: + description: |- + The base time that a host is ejected for. The real time is equal to the base time + multiplied by the number of times the host has been ejected and is capped by + max_ejection_time (Default 300s). Defaults to 30s. + type: string + enforcingConsecutive5xx: + description: |- + EnforcingConsecutive5xx is the % chance that a host will be actually ejected + when an outlier status is detected through consecutive 5xx. + This setting can be used to disable ejection or to ramp it up slowly. + Ex. Setting this to 10 will make it a 10% chance that the host will be ejected. + format: int32 + type: integer + interval: + description: |- + Interval between health check analysis sweeps. Each sweep may remove + hosts or return hosts to the pool. Ex. setting this to "10s" will set + the interval to 10 seconds. + type: string + maxEjectionPercent: + description: |- + The maximum % of an upstream cluster that can be ejected due to outlier detection. + Defaults to 10% but will eject at least one host regardless of the value. + format: int32 + type: integer + maxFailures: + description: |- + MaxFailures is the count of consecutive failures that results in a host + being removed from the pool. + format: int32 + type: integer + type: object + peer: + description: Peer is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides + config entry. + type: string + protocol: + description: |- + Protocol describes the upstream's service protocol. Valid values are "tcp", + "http" and "grpc". Anything else is treated as tcp. This enables protocol + aware features like per-request metrics and connection pooling, tracing, + routing etc. + type: string + type: object + type: array + type: object + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: |- + Conditions define a readiness condition for a Consul resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-serviceintentions.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-serviceintentions.yaml new file mode 100644 index 0000000000..72159ec187 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-serviceintentions.yaml @@ -0,0 +1,310 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: serviceintentions.consul.hashicorp.com +spec: + group: consul.hashicorp.com + names: + kind: ServiceIntentions + listKind: ServiceIntentionsList + plural: serviceintentions + shortNames: + - service-intentions + singular: serviceintentions + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: ServiceIntentions is the Schema for the serviceintentions 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: ServiceIntentionsSpec defines the desired state of ServiceIntentions. + properties: + destination: + description: Destination is the intention destination that will have + the authorization granted to. + properties: + name: + description: |- + Name is the destination of all intentions defined in this config entry. + This may be set to the wildcard character (*) to match + all services that don't otherwise have intentions defined. + type: string + namespace: + description: |- + Namespace specifies the namespace the config entry will apply to. + This may be set to the wildcard character (*) to match all services + in all namespaces that don't otherwise have intentions defined. + type: string + type: object + jwt: + description: JWT specifies the configuration to validate a JSON Web + Token for all incoming requests. + properties: + providers: + description: Providers is a list of providers to consider when + verifying a JWT. + items: + properties: + name: + description: |- + Name is the name of the JWT provider. There MUST be a corresponding + "jwt-provider" config entry with this name. + type: string + verifyClaims: + description: VerifyClaims is a list of additional claims + to verify in a JWT's payload. + items: + properties: + path: + description: Path is the path to the claim in the + token JSON. + items: + type: string + type: array + value: + description: |- + Value is the expected value at the given path. If the type at the path + is a list then we verify that this value is contained in the list. If + the type at the path is a string then we verify that this value matches. + type: string + type: object + type: array + type: object + type: array + type: object + sources: + description: |- + Sources is the list of all intention sources and the authorization granted to those sources. + The order of this list does not matter, but out of convenience Consul will always store this + reverse sorted by intention precedence, as that is the order that they will be evaluated at enforcement time. + items: + properties: + action: + description: |- + Action is required for an L4 intention, and should be set to one of + "allow" or "deny" for the action that should be taken if this intention matches a request. + type: string + description: + description: Description for the intention. This is not used + by Consul, but is presented in API responses to assist tooling. + type: string + name: + description: |- + Name is the source of the intention. This is the name of a + Consul service. The service doesn't need to be registered. + type: string + namespace: + description: Namespace is the namespace for the Name parameter. + type: string + partition: + description: Partition is the Admin Partition for the Name parameter. + type: string + peer: + description: Peer is the peer name for the Name parameter. + type: string + permissions: + description: |- + Permissions is the list of all additional L7 attributes that extend the intention match criteria. + Permission precedence is applied top to bottom. For any given request the first permission to match + in the list is terminal and stops further evaluation. As with L4 intentions, traffic that fails to + match any of the provided permissions in this intention will be subject to the default intention + behavior is defined by the default ACL policy. This should be omitted for an L4 intention + as it is mutually exclusive with the Action field. + items: + properties: + action: + description: |- + Action is one of "allow" or "deny" for the action that + should be taken if this permission matches a request. + type: string + http: + description: HTTP is a set of HTTP-specific authorization + criteria. + properties: + header: + description: |- + Header is a set of criteria that can match on HTTP request headers. + If more than one is configured all must match for the overall match to apply. + items: + properties: + exact: + description: Exact matches if the header with + the given name is this value. + type: string + invert: + description: Invert inverts the logic of the + match. + type: boolean + name: + description: Name is the name of the header + to match. + type: string + prefix: + description: Prefix matches if the header with + the given name has this prefix. + type: string + present: + description: Present matches if the header with + the given name is present with any value. + type: boolean + regex: + description: Regex matches if the header with + the given name matches this pattern. + type: string + suffix: + description: Suffix matches if the header with + the given name has this suffix. + type: string + type: object + type: array + methods: + description: |- + Methods is a list of HTTP methods for which this match applies. If unspecified + all HTTP methods are matched. If provided the names must be a valid method. + items: + type: string + type: array + pathExact: + description: PathExact is the exact path to match + on the HTTP request path. + type: string + pathPrefix: + description: PathPrefix is the path prefix to match + on the HTTP request path. + type: string + pathRegex: + description: PathRegex is the regular expression to + match on the HTTP request path. + type: string + type: object + jwt: + description: JWT specifies configuration to validate a + JSON Web Token for incoming requests. + properties: + providers: + description: Providers is a list of providers to consider + when verifying a JWT. + items: + properties: + name: + description: |- + Name is the name of the JWT provider. There MUST be a corresponding + "jwt-provider" config entry with this name. + type: string + verifyClaims: + description: VerifyClaims is a list of additional + claims to verify in a JWT's payload. + items: + properties: + path: + description: Path is the path to the claim + in the token JSON. + items: + type: string + type: array + value: + description: |- + Value is the expected value at the given path. If the type at the path + is a list then we verify that this value is contained in the list. If + the type at the path is a string then we verify that this value matches. + type: string + type: object + type: array + type: object + type: array + type: object + type: object + type: array + samenessGroup: + description: SamenessGroup is the name of the sameness group, + if applicable. + type: string + type: object + type: array + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: |- + Conditions define a readiness condition for a Consul resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-serviceresolvers.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-serviceresolvers.yaml new file mode 100644 index 0000000000..9367d6db2c --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-serviceresolvers.yaml @@ -0,0 +1,372 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: serviceresolvers.consul.hashicorp.com +spec: + group: consul.hashicorp.com + names: + kind: ServiceResolver + listKind: ServiceResolverList + plural: serviceresolvers + shortNames: + - service-resolver + singular: serviceresolver + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: ServiceResolver is the Schema for the serviceresolvers 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: ServiceResolverSpec defines the desired state of ServiceResolver. + properties: + connectTimeout: + description: |- + ConnectTimeout is the timeout for establishing new network connections + to this service. + type: string + defaultSubset: + description: |- + DefaultSubset is the subset to use when no explicit subset is requested. + If empty the unnamed subset is used. + type: string + failover: + additionalProperties: + properties: + datacenters: + description: Datacenters is a fixed list of datacenters to try + during failover. + items: + type: string + type: array + namespace: + description: |- + Namespace is the namespace to resolve the requested service from to form + the failover group of instances. If empty the current namespace is used. + type: string + policy: + description: Policy specifies the exact mechanism used for failover. + properties: + mode: + description: |- + Mode specifies the type of failover that will be performed. Valid values are + "sequential", "" (equivalent to "sequential") and "order-by-locality". + type: string + regions: + description: |- + Regions is the ordered list of the regions of the failover targets. + Valid values can be "us-west-1", "us-west-2", and so on. + items: + type: string + type: array + type: object + samenessGroup: + description: SamenessGroup is the name of the sameness group + to try during failover. + type: string + service: + description: |- + Service is the service to resolve instead of the default as the failover + group of instances during failover. + type: string + serviceSubset: + description: |- + ServiceSubset is the named subset of the requested service to resolve as + the failover group of instances. If empty the default subset for the + requested service is used. + type: string + targets: + description: Targets specifies a fixed list of failover targets + to try during failover. + items: + properties: + datacenter: + description: Datacenter specifies the datacenter to try + during failover. + type: string + namespace: + description: Namespace specifies the namespace to try + during failover. + type: string + partition: + description: Partition specifies the partition to try + during failover. + type: string + peer: + description: Peer specifies the name of the cluster peer + to try during failover. + type: string + service: + description: Service specifies the name of the service + to try during failover. + type: string + serviceSubset: + description: ServiceSubset specifies the service subset + to try during failover. + type: string + type: object + type: array + type: object + description: |- + Failover controls when and how to reroute traffic to an alternate pool of + service instances. + The map is keyed by the service subset it applies to and the special + string "*" is a wildcard that applies to any subset not otherwise + specified here. + type: object + loadBalancer: + description: |- + LoadBalancer determines the load balancing policy and configuration for services + issuing requests to this upstream service. + properties: + hashPolicies: + description: |- + HashPolicies is a list of hash policies to use for hashing load balancing algorithms. + Hash policies are evaluated individually and combined such that identical lists + result in the same hash. + If no hash policies are present, or none are successfully evaluated, + then a random backend host will be selected. + items: + properties: + cookieConfig: + description: CookieConfig contains configuration for the + "cookie" hash policy type. + properties: + path: + description: Path is the path to set for the cookie. + type: string + session: + description: Session determines whether to generate + a session cookie with no expiration. + type: boolean + ttl: + description: TTL is the ttl for generated cookies. Cannot + be specified for session cookies. + type: string + type: object + field: + description: |- + Field is the attribute type to hash on. + Must be one of "header", "cookie", or "query_parameter". + Cannot be specified along with sourceIP. + type: string + fieldValue: + description: |- + FieldValue is the value to hash. + ie. header name, cookie name, URL query parameter name + Cannot be specified along with sourceIP. + type: string + sourceIP: + description: |- + SourceIP determines whether the hash should be of the source IP rather than of a field and field value. + Cannot be specified along with field or fieldValue. + type: boolean + terminal: + description: |- + Terminal will short circuit the computation of the hash when multiple hash policies are present. + If a hash is computed when a Terminal policy is evaluated, + then that hash will be used and subsequent hash policies will be ignored. + type: boolean + type: object + type: array + leastRequestConfig: + description: LeastRequestConfig contains configuration for the + "leastRequest" policy type. + properties: + choiceCount: + description: ChoiceCount determines the number of random healthy + hosts from which to select the one with the least requests. + format: int32 + type: integer + type: object + policy: + description: Policy is the load balancing policy used to select + a host. + type: string + ringHashConfig: + description: RingHashConfig contains configuration for the "ringHash" + policy type. + properties: + maximumRingSize: + description: MaximumRingSize determines the maximum number + of entries in the hash ring. + format: int64 + type: integer + minimumRingSize: + description: MinimumRingSize determines the minimum number + of entries in the hash ring. + format: int64 + type: integer + type: object + type: object + prioritizeByLocality: + description: |- + PrioritizeByLocality controls whether the locality of services within the + local partition will be used to prioritize connectivity. + properties: + mode: + description: |- + Mode specifies the type of prioritization that will be performed + when selecting nodes in the local partition. + Valid values are: "" (default "none"), "none", and "failover". + type: string + type: object + redirect: + description: |- + Redirect when configured, all attempts to resolve the service this + resolver defines will be substituted for the supplied redirect + EXCEPT when the redirect has already been applied. + When substituting the supplied redirect, all other fields besides + Kind, Name, and Redirect will be ignored. + properties: + datacenter: + description: |- + Datacenter is the datacenter to resolve the service from instead of the + current one. + type: string + namespace: + description: |- + Namespace is the Consul namespace to resolve the service from instead of + the current namespace. If empty the current namespace is assumed. + type: string + partition: + description: |- + Partition is the Consul partition to resolve the service from instead of + the current partition. If empty the current partition is assumed. + type: string + peer: + description: |- + Peer is the name of the cluster peer to resolve the service from instead + of the current one. + type: string + samenessGroup: + description: SamenessGroup is the name of the sameness group to + resolve the service from instead of the current one. + type: string + service: + description: Service is a service to resolve instead of the current + service. + type: string + serviceSubset: + description: |- + ServiceSubset is a named subset of the given service to resolve instead + of one defined as that service's DefaultSubset If empty the default + subset is used. + type: string + type: object + requestTimeout: + description: |- + RequestTimeout is the timeout for receiving an HTTP response from this + service before the connection is terminated. + type: string + subsets: + additionalProperties: + properties: + filter: + description: |- + Filter is the filter expression to be used for selecting instances of the + requested service. If empty all healthy instances are returned. This + expression can filter on the same selectors as the Health API endpoint. + type: string + onlyPassing: + description: |- + OnlyPassing specifies the behavior of the resolver's health check + interpretation. If this is set to false, instances with checks in the + passing as well as the warning states will be considered healthy. If this + is set to true, only instances with checks in the passing state will be + considered healthy. + type: boolean + type: object + description: |- + Subsets is map of subset name to subset definition for all usable named + subsets of this service. The map key is the name of the subset and all + names must be valid DNS subdomain elements. + This may be empty, in which case only the unnamed default subset will + be usable. + type: object + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: |- + Conditions define a readiness condition for a Consul resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-servicerouters.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-servicerouters.yaml new file mode 100644 index 0000000000..4d62149682 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-servicerouters.yaml @@ -0,0 +1,335 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: servicerouters.consul.hashicorp.com +spec: + group: consul.hashicorp.com + names: + kind: ServiceRouter + listKind: ServiceRouterList + plural: servicerouters + shortNames: + - service-router + singular: servicerouter + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: ServiceRouter is the Schema for the servicerouters 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: ServiceRouterSpec defines the desired state of ServiceRouter. + properties: + routes: + description: |- + Routes are the list of routes to consider when processing L7 requests. + The first route to match in the list is terminal and stops further + evaluation. Traffic that fails to match any of the provided routes will + be routed to the default service. + items: + properties: + destination: + description: Destination controls how to proxy the matching + request(s) to a service. + properties: + idleTimeout: + description: |- + IdleTimeout is total amount of time permitted + for the request stream to be idle. + type: string + namespace: + description: |- + Namespace is the Consul namespace to resolve the service from instead of + the current namespace. If empty the current namespace is assumed. + type: string + numRetries: + description: NumRetries is the number of times to retry + the request when a retryable result occurs + format: int32 + type: integer + partition: + description: |- + Partition is the Consul partition to resolve the service from instead of + the current partition. If empty the current partition is assumed. + type: string + prefixRewrite: + description: |- + PrefixRewrite defines how to rewrite the HTTP request path before proxying + it to its final destination. + This requires that either match.http.pathPrefix or match.http.pathExact + be configured on this route. + type: string + requestHeaders: + description: Allow HTTP header manipulation to be configured. + properties: + add: + additionalProperties: + type: string + description: |- + Add is a set of name -> value pairs that should be appended to the request + or response (i.e. allowing duplicates if the same header already exists). + type: object + remove: + description: |- + Remove is the set of header names that should be stripped from the request + or response. + items: + type: string + type: array + set: + additionalProperties: + type: string + description: |- + Set is a set of name -> value pairs that should be added to the request or + response, overwriting any existing header values of the same name. + type: object + type: object + requestTimeout: + description: |- + RequestTimeout is the total amount of time permitted for the entire + downstream request (and retries) to be processed. + type: string + responseHeaders: + description: |- + HTTPHeaderModifiers is a set of rules for HTTP header modification that + should be performed by proxies as the request passes through them. It can + operate on either request or response headers depending on the context in + which it is used. + properties: + add: + additionalProperties: + type: string + description: |- + Add is a set of name -> value pairs that should be appended to the request + or response (i.e. allowing duplicates if the same header already exists). + type: object + remove: + description: |- + Remove is the set of header names that should be stripped from the request + or response. + items: + type: string + type: array + set: + additionalProperties: + type: string + description: |- + Set is a set of name -> value pairs that should be added to the request or + response, overwriting any existing header values of the same name. + type: object + type: object + retryOn: + description: |- + RetryOn is a flat list of conditions for Consul to retry requests based on the response from an upstream service. + Refer to the valid conditions here: https://developer.hashicorp.com/consul/docs/connect/config-entries/service-router#routes-destination-retryon + items: + type: string + type: array + retryOnConnectFailure: + description: RetryOnConnectFailure allows for connection + failure errors to trigger a retry. + type: boolean + retryOnStatusCodes: + description: RetryOnStatusCodes is a flat list of http response + status codes that are eligible for retry. + items: + format: int32 + type: integer + type: array + service: + description: |- + Service is the service to resolve instead of the default service. + If empty then the default service name is used. + type: string + serviceSubset: + description: |- + ServiceSubset is a named subset of the given service to resolve instead + of the one defined as that service's DefaultSubset. + If empty, the default subset is used. + type: string + type: object + match: + description: |- + Match is a set of criteria that can match incoming L7 requests. + If empty or omitted it acts as a catch-all. + properties: + http: + description: HTTP is a set of http-specific match criteria. + properties: + caseInsensitive: + description: CaseInsensitive configures PathExact and + PathPrefix matches to ignore upper/lower casing. + type: boolean + header: + description: |- + Header is a set of criteria that can match on HTTP request headers. + If more than one is configured all must match for the overall match to apply. + items: + properties: + exact: + description: Exact will match if the header with + the given name is this value. + type: string + invert: + description: Invert inverts the logic of the match. + type: boolean + name: + description: Name is the name of the header to + match. + type: string + prefix: + description: Prefix will match if the header with + the given name has this prefix. + type: string + present: + description: Present will match if the header + with the given name is present with any value. + type: boolean + regex: + description: Regex will match if the header with + the given name matches this pattern. + type: string + suffix: + description: Suffix will match if the header with + the given name has this suffix. + type: string + required: + - name + type: object + type: array + methods: + description: |- + Methods is a list of HTTP methods for which this match applies. + If unspecified all http methods are matched. + items: + type: string + type: array + pathExact: + description: PathExact is an exact path to match on + the HTTP request path. + type: string + pathPrefix: + description: PathPrefix is a path prefix to match on + the HTTP request path. + type: string + pathRegex: + description: PathRegex is a regular expression to match + on the HTTP request path. + type: string + queryParam: + description: |- + QueryParam is a set of criteria that can match on HTTP query parameters. + If more than one is configured all must match for the overall match to apply. + items: + properties: + exact: + description: Exact will match if the query parameter + with the given name is this value. + type: string + name: + description: Name is the name of the query parameter + to match on. + type: string + present: + description: |- + Present will match if the query parameter with the given name is present + with any value. + type: boolean + regex: + description: Regex will match if the query parameter + with the given name matches this pattern. + type: string + required: + - name + type: object + type: array + type: object + type: object + type: object + type: array + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: |- + Conditions define a readiness condition for a Consul resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-servicesplitters.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-servicesplitters.yaml new file mode 100644 index 0000000000..704ad5df98 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-servicesplitters.yaml @@ -0,0 +1,194 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: servicesplitters.consul.hashicorp.com +spec: + group: consul.hashicorp.com + names: + kind: ServiceSplitter + listKind: ServiceSplitterList + plural: servicesplitters + shortNames: + - service-splitter + singular: servicesplitter + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: ServiceSplitter is the Schema for the servicesplitters 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: ServiceSplitterSpec defines the desired state of ServiceSplitter. + properties: + splits: + description: |- + Splits defines how much traffic to send to which set of service instances during a traffic split. + The sum of weights across all splits must add up to 100. + items: + properties: + namespace: + description: |- + Namespace is the Consul namespace to resolve the service from instead of + the current namespace. If empty the current namespace is assumed. + type: string + partition: + description: |- + Partition is the Consul partition to resolve the service from instead of + the current partition. If empty the current partition is assumed. + type: string + requestHeaders: + description: Allow HTTP header manipulation to be configured. + properties: + add: + additionalProperties: + type: string + description: |- + Add is a set of name -> value pairs that should be appended to the request + or response (i.e. allowing duplicates if the same header already exists). + type: object + remove: + description: |- + Remove is the set of header names that should be stripped from the request + or response. + items: + type: string + type: array + set: + additionalProperties: + type: string + description: |- + Set is a set of name -> value pairs that should be added to the request or + response, overwriting any existing header values of the same name. + type: object + type: object + responseHeaders: + description: |- + HTTPHeaderModifiers is a set of rules for HTTP header modification that + should be performed by proxies as the request passes through them. It can + operate on either request or response headers depending on the context in + which it is used. + properties: + add: + additionalProperties: + type: string + description: |- + Add is a set of name -> value pairs that should be appended to the request + or response (i.e. allowing duplicates if the same header already exists). + type: object + remove: + description: |- + Remove is the set of header names that should be stripped from the request + or response. + items: + type: string + type: array + set: + additionalProperties: + type: string + description: |- + Set is a set of name -> value pairs that should be added to the request or + response, overwriting any existing header values of the same name. + type: object + type: object + service: + description: Service is the service to resolve instead of the + default. + type: string + serviceSubset: + description: |- + ServiceSubset is a named subset of the given service to resolve instead of one defined + as that service's DefaultSubset. If empty the default subset is used. + type: string + weight: + description: |- + Weight is a value between 0 and 100 reflecting what portion of traffic should be directed to this split. + The smallest representable weight is 1/10000 or .01%. + type: number + type: object + type: array + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: |- + Conditions define a readiness condition for a Consul resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-tcproutes-external.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-tcproutes-external.yaml new file mode 100644 index 0000000000..a57a329a56 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-tcproutes-external.yaml @@ -0,0 +1,281 @@ +{{- if and .Values.connectInject.enabled (or .Values.connectInject.apiGateway.manageExternalCRDs .Values.connectInject.apiGateway.manageNonStandardCRDs ) }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 + gateway.networking.k8s.io/bundle-version: v0.6.2 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: tcproutes.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: TCPRoute + listKind: TCPRouteList + plural: tcproutes + singular: tcproute + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha2 + schema: + openAPIV3Schema: + description: TCPRoute provides a way to route TCP requests. When combined with a Gateway listener, it can be used to forward connections on the port specified by the listener to a set of backends specified by the TCPRoute. + 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 TCPRoute. + properties: + 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. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n 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. \n 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 any other kind 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). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n 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). \n 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. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n 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. \n 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. \n 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. \n 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. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n 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. \n Support: Extended \n " + 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: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n 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. \n 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 + rules: + description: Rules are a list of TCP matchers and actions. + items: + description: TCPRouteRule is the configuration for a given rule. + properties: + backendRefs: + description: "BackendRefs defines the backend(s) where matching requests should be sent. If unspecified or invalid (refers to a non-existent resource or a Service with no endpoints), the underlying implementation MUST actively reject connection attempts to this backend. Connection rejections must respect weight; if an invalid backend is requested to have 80% of connections, then 80% of connections must be rejected instead. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Extended" + items: + description: "BackendRef defines how a Route should forward a request to a Kubernetes resource. \n Note that when a 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." + 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 kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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. \n Note that when a 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. \n 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. \n 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. \n Support for this field varies based on the context where used." + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + maxItems: 16 + minItems: 1 + type: array + type: object + maxItems: 16 + minItems: 1 + type: array + required: + - rules + type: object + status: + description: Status defines the current state of TCPRoute. + 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. \n 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. \n 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. \n 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. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * 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. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \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. \n Example: \"example.net/gateway-controller\". \n 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). \n 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). \n 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. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n 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. \n 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. \n 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. \n 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. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n 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. \n Support: Extended \n " + 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: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n 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. \n 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: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-tcproutes.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-tcproutes.yaml new file mode 100644 index 0000000000..a71d31206c --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-tcproutes.yaml @@ -0,0 +1,299 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: tcproutes.mesh.consul.hashicorp.com +spec: + group: mesh.consul.hashicorp.com + names: + kind: TCPRoute + listKind: TCPRouteList + plural: tcproutes + shortNames: + - tcp-route + singular: tcproute + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v2beta1 + schema: + openAPIV3Schema: + description: TCPRoute is the Schema for the TCP Route 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: |- + NOTE: this should align to the GAMMA/gateway-api version, or at least be + easily translatable. + + + https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.TCPRoute + + + This is a Resource type. + properties: + parentRefs: + description: |- + ParentRefs references the resources (usually Services) that a Route wants + to be attached to. + + + It is invalid to reference an identical parent more than once. It is valid + to reference multiple distinct sections within the same parent resource. + items: + description: 'NOTE: roughly equivalent to structs.ResourceReference' + properties: + port: + description: |- + For east/west this is the name of the Consul Service port to direct traffic to + or empty to imply all. + For north/south this is TBD. + + + For more details on potential values of this field, see documentation for + Service.ServicePort. + type: string + ref: + description: |- + For east/west configuration, this should point to a Service. + For north/south it should point to a Gateway. + properties: + name: + description: Name is the user-given name of the resource + (e.g. the "billing" service). + type: string + section: + description: Section identifies which part of the resource + the condition relates to. + type: string + tenancy: + description: |- + Tenancy identifies the tenancy units (i.e. partition, namespace) in which + the resource resides. + properties: + namespace: + description: |- + Namespace further isolates resources within a partition. + https://developer.hashicorp.com/consul/docs/enterprise/namespaces + + + When using the List and WatchList endpoints, provide the wildcard value "*" + to list resources across all namespaces. + type: string + partition: + description: |- + Partition is the topmost administrative boundary within a cluster. + https://developer.hashicorp.com/consul/docs/enterprise/admin-partitions + + + When using the List and WatchList endpoints, provide the wildcard value "*" + to list resources across all partitions. + type: string + type: object + type: + description: Type identifies the resource's type. + properties: + group: + description: |- + Group describes the area of functionality to which this resource type + relates (e.g. "catalog", "authorization"). + type: string + groupVersion: + description: |- + GroupVersion is incremented when sweeping or backward-incompatible changes + are made to the group's resource types. + type: string + kind: + description: Kind identifies the specific resource type + within the group. + type: string + type: object + type: object + type: object + type: array + rules: + description: Rules are a list of TCP matchers and actions. + items: + properties: + backendRefs: + description: |- + BackendRefs defines the backend(s) where matching requests should be sent. + If unspecified or invalid (refers to a non-existent resource or a Service + with no endpoints), the underlying implementation MUST actively reject + connection attempts to this backend. Connection rejections must respect + weight; if an invalid backend is requested to have 80% of connections, + then 80% of connections must be rejected instead. + items: + properties: + backendRef: + properties: + datacenter: + type: string + port: + description: |- + For east/west this is the name of the Consul Service port to direct traffic to + or empty to imply using the same value as the parent ref. + For north/south this is TBD. + + + For more details on potential values of this field, see documentation for + Service.ServicePort. + type: string + ref: + description: For east/west configuration, this should + point to a Service. + properties: + name: + description: Name is the user-given name of the + resource (e.g. the "billing" service). + type: string + section: + description: Section identifies which part of + the resource the condition relates to. + type: string + tenancy: + description: |- + Tenancy identifies the tenancy units (i.e. partition, namespace) in which + the resource resides. + properties: + namespace: + description: |- + Namespace further isolates resources within a partition. + https://developer.hashicorp.com/consul/docs/enterprise/namespaces + + + When using the List and WatchList endpoints, provide the wildcard value "*" + to list resources across all namespaces. + type: string + partition: + description: |- + Partition is the topmost administrative boundary within a cluster. + https://developer.hashicorp.com/consul/docs/enterprise/admin-partitions + + + When using the List and WatchList endpoints, provide the wildcard value "*" + to list resources across all partitions. + type: string + type: object + type: + description: Type identifies the resource's type. + properties: + group: + description: |- + Group describes the area of functionality to which this resource type + relates (e.g. "catalog", "authorization"). + type: string + groupVersion: + description: |- + GroupVersion is incremented when sweeping or backward-incompatible changes + are made to the group's resource types. + type: string + kind: + description: Kind identifies the specific + resource type within the group. + type: string + type: object + type: object + type: object + weight: + 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. + format: int32 + type: integer + type: object + type: array + type: object + type: array + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: |- + Conditions define a readiness condition for a Consul resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-terminatinggateways.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-terminatinggateways.yaml new file mode 100644 index 0000000000..5d78a50ca6 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-terminatinggateways.yaml @@ -0,0 +1,148 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: terminatinggateways.consul.hashicorp.com +spec: + group: consul.hashicorp.com + names: + kind: TerminatingGateway + listKind: TerminatingGatewayList + plural: terminatinggateways + shortNames: + - terminating-gateway + singular: terminatinggateway + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: TerminatingGateway is the Schema for the terminatinggateways + 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: TerminatingGatewaySpec defines the desired state of TerminatingGateway. + properties: + services: + description: Services is a list of service names represented by the + terminating gateway. + items: + description: A LinkedService is a service represented by a terminating + gateway. + properties: + caFile: + description: |- + CAFile is the optional path to a CA certificate to use for TLS connections + from the gateway to the linked service. + type: string + certFile: + description: |- + CertFile is the optional path to a client certificate to use for TLS connections + from the gateway to the linked service. + type: string + disableAutoHostRewrite: + description: DisableAutoHostRewrite disables terminating gateways + auto host rewrite feature when set to true. + type: boolean + keyFile: + description: |- + KeyFile is the optional path to a private key to use for TLS connections + from the gateway to the linked service. + type: string + name: + description: Name is the name of the service, as defined in + Consul's catalog. + type: string + namespace: + description: The namespace the service is registered in. + type: string + sni: + description: SNI is the optional name to specify during the + TLS handshake with a linked service. + type: string + type: object + type: array + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: |- + Conditions define a readiness condition for a Consul resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-tlsroutes-external.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-tlsroutes-external.yaml new file mode 100644 index 0000000000..1acd1b973a --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-tlsroutes-external.yaml @@ -0,0 +1,291 @@ +{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 + gateway.networking.k8s.io/bundle-version: v0.6.2 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: tlsroutes.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: TLSRoute + listKind: TLSRouteList + plural: tlsroutes + singular: tlsroute + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha2 + schema: + openAPIV3Schema: + description: "The TLSRoute resource is similar to TCPRoute, but can be configured to match against TLS-specific metadata. This allows more flexibility in matching streams for a given TLS listener. \n If you need to forward traffic to a single target for a TLS listener, you could choose to use a TCPRoute with a TLS listener." + 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 TLSRoute. + properties: + hostnames: + description: "Hostnames defines a set of SNI names that should match against the SNI attribute of TLS ClientHello message in TLS handshake. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed in SNI names per RFC 6066. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n If a hostname is specified by both the Listener and TLSRoute, there must be at least one intersecting hostname for the TLSRoute to be attached to the Listener. For example: \n * A Listener with `test.example.com` as the hostname matches TLSRoutes 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 TLSRoutes 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. \n If both the Listener and TLSRoute have specified hostnames, any TLSRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the TLSRoute specified `test.example.com` and `test.example.net`, `test.example.net` must not be considered for a match. \n If both the Listener and TLSRoute have specified hostnames, and none match with the criteria above, then the TLSRoute is not accepted. The implementation must raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. \n 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: \n 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. \n 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`). \n 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. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n 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. \n 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 any other kind 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). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n 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). \n 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. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n 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. \n 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. \n 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. \n 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. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n 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. \n Support: Extended \n " + 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: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n 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. \n 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 + rules: + description: Rules are a list of TLS matchers and actions. + items: + description: TLSRouteRule is the configuration for a given rule. + properties: + backendRefs: + description: "BackendRefs defines the backend(s) where matching requests should be sent. If unspecified or invalid (refers to a non-existent resource or a Service with no endpoints), the rule performs no forwarding; if no filters are specified that would result in a response being sent, the underlying implementation must actively reject request attempts to this backend, by rejecting the connection or returning a 500 status code. Request rejections must respect weight; if an invalid backend is requested to have 80% of requests, then 80% of requests must be rejected instead. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Extended" + items: + description: "BackendRef defines how a Route should forward a request to a Kubernetes resource. \n Note that when a 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." + 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 kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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. \n Note that when a 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. \n 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. \n 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. \n Support for this field varies based on the context where used." + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + maxItems: 16 + minItems: 1 + type: array + type: object + maxItems: 16 + minItems: 1 + type: array + required: + - rules + type: object + status: + description: Status defines the current state of TLSRoute. + 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. \n 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. \n 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. \n 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. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * 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. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \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. \n Example: \"example.net/gateway-controller\". \n 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). \n 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). \n 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. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n 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. \n 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. \n 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. \n 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. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n 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. \n Support: Extended \n " + 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: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n 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. \n 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: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-trafficpermissions.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-trafficpermissions.yaml new file mode 100644 index 0000000000..2a0b069dbc --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-trafficpermissions.yaml @@ -0,0 +1,280 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: trafficpermissions.auth.consul.hashicorp.com +spec: + group: auth.consul.hashicorp.com + names: + kind: TrafficPermissions + listKind: TrafficPermissionsList + plural: trafficpermissions + shortNames: + - traffic-permissions + singular: trafficpermissions + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v2beta1 + schema: + openAPIV3Schema: + description: TrafficPermissions is the Schema for the traffic-permissions + 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: TrafficPermissions authorizes traffic between workloads in + a Consul service mesh. + properties: + action: + description: |- + Action can be either allow or deny for the entire object. It will default to allow. + Deny actions are available only in Consul Enterprise. + + + If action is allow, we will allow the connection if one of the rules in Rules matches, in other words, we will deny + all requests except for the ones that match Rules. If Consul is in default allow mode, then allow + actions have no effect without a deny permission as everything is allowed by default. + + + If action is deny, we will deny the connection if one of the rules in Rules match, in other words, + we will allow all requests except for the ones that match Rules. If Consul is default deny mode, + then deny permissions have no effect without an allow permission as everything is denied by default. + + + Action unspecified is reserved for compatibility with the addition of future actions. + enum: + - ACTION_ALLOW + - ACTION_DENY + - ACTION_UNKNOWN + format: int32 + type: string + destination: + description: |- + Destination is a configuration of the destination proxies + where these traffic permissions should apply. + properties: + identityName: + type: string + type: object + permissions: + description: Permissions is a list of permissions to match on. They + are applied using OR semantics. + items: + description: Permissions is a list of permissions to match on. + properties: + destinationRules: + description: |- + DestinationRules is a list of rules to apply for matching sources in this Permission. + These rules are specific to the request or connection that is going to the destination(s) + selected by the TrafficPermissions resource. + items: + description: DestinationRule contains rules rules to apply + to the incoming connection. + properties: + exclude: + description: Exclude contains a list of rules to exclude + when evaluating rules for the incoming connection. + items: + properties: + headers: + items: + properties: + exact: + type: string + invert: + type: boolean + name: + type: string + prefix: + type: string + present: + type: boolean + regex: + type: string + suffix: + type: string + type: object + type: array + methods: + description: Methods is the list of HTTP methods. + items: + type: string + type: array + pathExact: + type: string + pathPrefix: + type: string + pathRegex: + type: string + portNames: + description: |- + PortNames is a list of workload ports to apply this rule to. The ports specified here + must be the ports used in the connection. + items: + type: string + type: array + type: object + type: array + headers: + items: + properties: + exact: + type: string + invert: + type: boolean + name: + type: string + prefix: + type: string + present: + type: boolean + regex: + type: string + suffix: + type: string + type: object + type: array + methods: + description: |- + Methods is the list of HTTP methods. If no methods are specified, + this rule will apply to all methods. + items: + type: string + type: array + pathExact: + type: string + pathPrefix: + type: string + pathRegex: + type: string + portNames: + items: + type: string + type: array + type: object + type: array + sources: + description: Sources is a list of sources in this traffic permission. + items: + description: |- + Source represents the source identity. + To specify any of the wildcard sources, the specific fields need to be omitted. + For example, for a wildcard namespace, identity_name should be omitted. + properties: + exclude: + description: Exclude is a list of sources to exclude from + this source. + items: + description: |- + ExcludeSource is almost the same as source but it prevents the addition of + matching sources. + properties: + identityName: + type: string + namespace: + type: string + partition: + type: string + peer: + type: string + samenessGroup: + type: string + type: object + type: array + identityName: + type: string + namespace: + type: string + partition: + type: string + peer: + type: string + samenessGroup: + type: string + type: object + type: array + type: object + type: array + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: |- + Conditions define a readiness condition for a Consul resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/crd-udproutes-external.yaml b/charts/hashicorp/consul/1.5.3/templates/crd-udproutes-external.yaml new file mode 100644 index 0000000000..0661b24c1a --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/crd-udproutes-external.yaml @@ -0,0 +1,281 @@ +{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 + gateway.networking.k8s.io/bundle-version: v0.6.2 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: udproutes.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: UDPRoute + listKind: UDPRouteList + plural: udproutes + singular: udproute + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha2 + schema: + openAPIV3Schema: + description: UDPRoute provides a way to route UDP traffic. When combined with a Gateway listener, it can be used to forward traffic on the port specified by the listener to a set of backends specified by the UDPRoute. + 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 UDPRoute. + properties: + 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. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n 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. \n 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 any other kind 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). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n 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). \n 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. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n 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. \n 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. \n 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. \n 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. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n 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. \n Support: Extended \n " + 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: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n 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. \n 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 + rules: + description: Rules are a list of UDP matchers and actions. + items: + description: UDPRouteRule is the configuration for a given rule. + properties: + backendRefs: + description: "BackendRefs defines the backend(s) where matching requests should be sent. If unspecified or invalid (refers to a non-existent resource or a Service with no endpoints), the underlying implementation MUST actively reject connection attempts to this backend. Packet drops must respect weight; if an invalid backend is requested to have 80% of the packets, then 80% of packets must be dropped instead. \n Support: Core for Kubernetes Service Support: Implementation-specific for any other resource \n Support for weight: Extended" + items: + description: "BackendRef defines how a Route should forward a request to a Kubernetes resource. \n Note that when a 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." + 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 kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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. \n Note that when a 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. \n 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. \n 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. \n Support for this field varies based on the context where used." + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + maxItems: 16 + minItems: 1 + type: array + type: object + maxItems: 16 + minItems: 1 + type: array + required: + - rules + type: object + status: + description: Status defines the current state of UDPRoute. + 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. \n 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. \n 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. \n 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. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * 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. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \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. \n Example: \"example.net/gateway-controller\". \n 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). \n 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). \n 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. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n 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. \n 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. \n 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. \n 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. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n 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. \n Support: Extended \n " + 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: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n 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. \n 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: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/create-federation-secret-job.yaml b/charts/hashicorp/consul/1.5.3/templates/create-federation-secret-job.yaml new file mode 100644 index 0000000000..2092b97852 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/create-federation-secret-job.yaml @@ -0,0 +1,147 @@ +{{- if .Values.global.federation.createFederationSecret }} +{{- if not .Values.global.federation.enabled }}{{ fail "global.federation.enabled must be true when global.federation.createFederationSecret is true" }}{{ end }} +{{- if and (not .Values.global.acls.createReplicationToken) .Values.global.acls.manageSystemACLs }}{{ fail "global.acls.createReplicationToken must be true when global.acls.manageSystemACLs is true because the federation secret must include the replication token" }}{{ end }} +{{- if eq (int .Values.server.updatePartition) 0 }} +{{ template "consul.validateRequiredCloudSecretsExist" . }} +{{ template "consul.validateCloudSecretKeys" . }} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "consul.fullname" . }}-create-federation-secret + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: create-federation-secret + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} + annotations: + "helm.sh/hook": post-install,post-upgrade + {{- /* Hook weight needs to be 1 so that the service account is provisioned first */}} + "helm.sh/hook-weight": "1" + "helm.sh/hook-delete-policy": hook-succeeded +spec: + template: + metadata: + name: {{ template "consul.fullname" . }}-create-federation-secret + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: create-federation-secret + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} + annotations: + "consul.hashicorp.com/connect-inject": "false" + "consul.hashicorp.com/mesh-inject": "false" + spec: + restartPolicy: Never + serviceAccountName: {{ template "consul.fullname" . }}-create-federation-secret + {{- if .Values.client.tolerations }} + tolerations: + {{ tpl .Values.client.tolerations . | nindent 8 | trim }} + {{- end }} + {{- if .Values.client.priorityClassName }} + priorityClassName: {{ .Values.client.priorityClassName | quote }} + {{- end }} + {{- if .Values.client.nodeSelector }} + nodeSelector: + {{ tpl .Values.client.nodeSelector . | indent 8 | trim }} + {{- end }} + volumes: + {{- /* We can assume tls is enabled because there is a check in server-statefulset + that requires tls to be enabled if federation is enabled. */}} + - name: consul-ca-cert + secret: + {{- if .Values.global.tls.caCert.secretName }} + secretName: {{ .Values.global.tls.caCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" . }}-ca-cert + {{- end }} + items: + - key: {{ default "tls.crt" .Values.global.tls.caCert.secretKey }} + path: tls.crt + - name: consul-ca-key + secret: + {{- if .Values.global.tls.caKey.secretName }} + secretName: {{ .Values.global.tls.caKey.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" . }}-ca-key + {{- end }} + items: + - key: {{ default "tls.key" .Values.global.tls.caKey.secretKey }} + path: tls.key + {{- if (and .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey) }} + - name: gossip-encryption-key + secret: + secretName: {{ .Values.global.gossipEncryption.secretName }} + items: + - key: {{ .Values.global.gossipEncryption.secretKey }} + path: gossip.key + {{- else if .Values.global.gossipEncryption.autoGenerate }} + - name: gossip-encryption-key + secret: + secretName: {{ template "consul.fullname" . }}-gossip-encryption-key + items: + - key: key + path: gossip.key + {{- end }} + + containers: + - name: create-federation-secret + image: "{{ .Values.global.imageK8S }}" + {{ template "consul.imagePullPolicy" . }} + {{- include "consul.restrictedSecurityContext" . | nindent 10 }} + env: + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: CONSUL_HTTP_ADDR + value: "https://{{ template "consul.fullname" . }}-server.{{ .Release.Namespace }}.svc:8501" + - name: CONSUL_CACERT + value: /consul/tls/ca/tls.crt + volumeMounts: + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: true + - name: consul-ca-key + mountPath: /consul/tls/server/ca + readOnly: true + {{- if (or .Values.global.gossipEncryption.autoGenerate (and .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey)) }} + - name: gossip-encryption-key + mountPath: /consul/gossip + readOnly: true + {{- end }} + command: + - "/bin/sh" + - "-ec" + - | + exec consul-k8s-control-plane create-federation-secret \ + -log-level={{ default .Values.global.logLevel .Values.global.federation.logLevel }} \ + -log-json={{ .Values.global.logJSON }} \ + {{- if (or .Values.global.gossipEncryption.autoGenerate (and .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey)) }} + -gossip-key-file=/consul/gossip/gossip.key \ + {{- end }} + {{- if .Values.global.acls.createReplicationToken }} + -export-replication-token=true \ + {{- end }} + -mesh-gateway-service-name={{ .Values.meshGateway.consulServiceName }} \ + -k8s-namespace="${NAMESPACE}" \ + -resource-prefix="{{ template "consul.fullname" . }}" \ + -server-ca-cert-file=/consul/tls/ca/tls.crt \ + -server-ca-key-file=/consul/tls/server/ca/tls.key \ + -consul-api-timeout={{ .Values.global.consulAPITimeout }} + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/create-federation-secret-podsecuritypolicy.yaml b/charts/hashicorp/consul/1.5.3/templates/create-federation-secret-podsecuritypolicy.yaml new file mode 100644 index 0000000000..8217311992 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/create-federation-secret-podsecuritypolicy.yaml @@ -0,0 +1,42 @@ +{{- if .Values.global.enablePodSecurityPolicies }} +{{- if .Values.global.federation.createFederationSecret }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-create-federation-secret + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: create-federation-secret + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-delete-policy": hook-succeeded,before-hook-creation +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + # Allow core volume types. + volumes: + - 'secret' + - 'emptyDir' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/create-federation-secret-role.yaml b/charts/hashicorp/consul/1.5.3/templates/create-federation-secret-role.yaml new file mode 100644 index 0000000000..086932a831 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/create-federation-secret-role.yaml @@ -0,0 +1,49 @@ +{{- if .Values.global.federation.createFederationSecret }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" . }}-create-federation-secret + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: create-federation-secret + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-delete-policy": hook-succeeded,before-hook-creation +rules: + {{/* Must have separate rule for create secret permissions vs update because + can't set resourceNames for create (https://github.com/kubernetes/kubernetes/issues/80295) */}} + - apiGroups: [""] + resources: + - secrets + verbs: + - create + - apiGroups: [""] + resources: + - secrets + resourceNames: + - {{ template "consul.fullname" . }}-federation + verbs: + - update + {{- if .Values.global.acls.manageSystemACLs }} + - apiGroups: [""] + resources: + - secrets + resourceNames: + - {{ template "consul.fullname" . }}-acl-replication-acl-token + verbs: + - get + {{- end }} + {{- if .Values.global.enablePodSecurityPolicies }} + - apiGroups: ["policy"] + resources: + - podsecuritypolicies + verbs: + - use + resourceNames: + - {{ template "consul.fullname" . }}-create-federation-secret + {{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/create-federation-secret-rolebinding.yaml b/charts/hashicorp/consul/1.5.3/templates/create-federation-secret-rolebinding.yaml new file mode 100644 index 0000000000..3db8e7cb06 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/create-federation-secret-rolebinding.yaml @@ -0,0 +1,23 @@ +{{- if .Values.global.federation.createFederationSecret }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" . }}-create-federation-secret + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: create-federation-secret + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-delete-policy": hook-succeeded,before-hook-creation +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" . }}-create-federation-secret +subjects: +- kind: ServiceAccount + name: {{ template "consul.fullname" . }}-create-federation-secret +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/create-federation-secret-serviceaccount.yaml b/charts/hashicorp/consul/1.5.3/templates/create-federation-secret-serviceaccount.yaml new file mode 100644 index 0000000000..e398ec69c4 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/create-federation-secret-serviceaccount.yaml @@ -0,0 +1,22 @@ +{{- if .Values.global.federation.createFederationSecret }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-create-federation-secret + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: create-federation-secret + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-delete-policy": hook-succeeded,before-hook-creation +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/datadog-agent-role.yaml b/charts/hashicorp/consul/1.5.3/templates/datadog-agent-role.yaml new file mode 100644 index 0000000000..191e6433c6 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/datadog-agent-role.yaml @@ -0,0 +1,38 @@ +{{- if .Values.global.metrics.datadog.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" . }}-datadog-metrics + namespace: {{ .Release.Namespace }} + labels: + app: datadog + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: agent +{{- if (or (and .Values.global.openshift.enabled .Values.server.exposeGossipAndRPCPorts) .Values.global.enablePodSecurityPolicies) }} +{{- if .Values.global.enablePodSecurityPolicies }} +rules: + - apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + resourceNames: + - {{ template "consul.fullname" . }}-datadog-metrics + verbs: + - use +{{- end }} +{{- if (and .Values.global.openshift.enabled .Values.server.exposeGossipAndRPCPorts ) }} + - apiGroups: ["security.openshift.io"] + resources: ["securitycontextconstraints"] + resourceNames: + - {{ template "consul.fullname" . }}-datadog-metrics + verbs: + - use +{{- end }} +{{- else}} +rules: + - apiGroups: [ "" ] + resources: [ "secrets" ] + resourceNames: + - {{ .Release.Namespace }}-datadog-agent-metrics-acl-token + verbs: [ "get", "watch", "list" ] +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/hashicorp/consul/1.5.3/templates/datadog-agent-rolebinding.yaml b/charts/hashicorp/consul/1.5.3/templates/datadog-agent-rolebinding.yaml new file mode 100644 index 0000000000..5fc3fdf545 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/datadog-agent-rolebinding.yaml @@ -0,0 +1,26 @@ +{{- if .Values.global.metrics.datadog.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" . }}-datadog-metrics + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: agent +subjects: + - kind: ServiceAccount + apiGroup: "" + name: datadog-agent + namespace: datadog + - kind: ServiceAccount + apiGroup: "" + name: datadog-cluster-agent + namespace: datadog +roleRef: + kind: Role + name: {{ template "consul.fullname" . }}-datadog-metrics + apiGroup: "" +{{- end }} \ No newline at end of file diff --git a/charts/hashicorp/consul/1.5.3/templates/dns-service.yaml b/charts/hashicorp/consul/1.5.3/templates/dns-service.yaml new file mode 100644 index 0000000000..5bb446bc19 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/dns-service.yaml @@ -0,0 +1,41 @@ +{{- if (or (and (ne (.Values.dns.enabled | toString) "-") .Values.dns.enabled) (and (eq (.Values.dns.enabled | toString) "-") .Values.connectInject.transparentProxy.defaultEnabled)) }} +# Service for Consul DNS. +apiVersion: v1 +kind: Service +metadata: + name: {{ template "consul.fullname" . }}-dns + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: dns + {{- if .Values.dns.annotations }} + annotations: + {{ tpl .Values.dns.annotations . | nindent 4 | trim }} + {{- end }} +spec: +{{- if .Values.dns.type }} + type: {{ .Values.dns.type }} +{{- end }} +{{- if .Values.dns.clusterIP }} + clusterIP: {{ .Values.dns.clusterIP }} +{{- end }} + ports: + - name: dns-tcp + port: 53 + protocol: "TCP" + targetPort: dns-tcp + - name: dns-udp + port: 53 + protocol: "UDP" + targetPort: dns-udp + selector: + app: {{ template "consul.name" . }} + release: "{{ .Release.Name }}" + hasDNS: "true" + {{- if .Values.dns.additionalSpec }} + {{ tpl .Values.dns.additionalSpec . | nindent 2 | trim }} + {{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/enterprise-license-job.yaml b/charts/hashicorp/consul/1.5.3/templates/enterprise-license-job.yaml new file mode 100644 index 0000000000..9dd0281978 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/enterprise-license-job.yaml @@ -0,0 +1,147 @@ +{{- if .Values.server.enterpriseLicense }}{{ fail "server.enterpriseLicense has been moved to global.enterpriseLicense" }}{{ end -}} +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey (not .Values.global.enterpriseLicense.enableLicenseAutoload)) }} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "consul.fullname" . }}-enterprise-license + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/managed-by: {{.Release.Service | quote }} + app.kubernetes.io/instance: {{.Release.Name | quote }} + helm.sh/chart: "{{.Chart.Name}}-{{.Chart.Version}}" + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: license + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-weight": "100" + "helm.sh/hook-delete-policy": hook-succeeded +spec: + template: + metadata: + name: {{ template "consul.fullname" . }}-license + labels: + app.kubernetes.io/managed-by: {{.Release.Service | quote }} + app.kubernetes.io/instance: {{.Release.Name | quote }} + helm.sh/chart: "{{.Chart.Name}}-{{.Chart.Version}}" + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: license + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} + annotations: + "consul.hashicorp.com/connect-inject": "false" + "consul.hashicorp.com/mesh-inject": "false" + spec: + restartPolicy: Never + serviceAccountName: {{ template "consul.fullname" . }}-enterprise-license + {{- if .Values.global.tls.enabled }} + volumes: + - name: consul-ca-cert + secret: + {{- if .Values.global.tls.caCert.secretName }} + secretName: {{ .Values.global.tls.caCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" . }}-ca-cert + {{- end }} + items: + - key: {{ default "tls.crt" .Values.global.tls.caCert.secretKey }} + path: tls.crt + {{- end }} + containers: + - name: apply-enterprise-license + image: "{{ default .Values.global.image .Values.server.image }}" + {{ template "consul.imagePullPolicy" . }} + env: + - name: ENTERPRISE_LICENSE + {{- if .Values.global.secretsBackend.vault.enabled }} + value: /vault/secrets/enterpriselicense.txt + {{- else }} + valueFrom: + secretKeyRef: + name: {{ .Values.global.enterpriseLicense.secretName }} + key: {{ .Values.global.enterpriseLicense.secretKey }} + {{- end }} + - name: CONSUL_HTTP_ADDR + {{- if .Values.global.tls.enabled }} + value: https://{{ template "consul.fullname" . }}-server:8501 + {{- else }} + value: http://{{ template "consul.fullname" . }}-server:8500 + {{- end }} + {{- if .Values.global.tls.enabled }} + - name: CONSUL_CACERT + value: /consul/tls/ca/tls.crt + {{- end}} + {{- if .Values.global.acls.manageSystemACLs }} + - name: CONSUL_HTTP_TOKEN + valueFrom: + secretKeyRef: + name: "{{ template "consul.fullname" . }}-enterprise-license-acl-token" + key: "token" + {{- end}} + command: + - "/bin/sh" + - "-c" + - | + # Create a script that we can execute with the timeout command. + mkdir -p /tmp/scripts/ + cat > /tmp/scripts/apply-license.sh << 'EOF' + #!/bin/sh + while true; do + echo "Applying license..." + if consul license put "${ENTERPRISE_LICENSE}" 2>&1; then + echo "License applied successfully" + break + fi + echo "Retrying in 2s..." + sleep 2 + done + EOF + chmod +x /tmp/scripts/apply-license.sh + + # Time out after 20 minutes. Use || to support new timeout versions that don't accept -t + timeout -t 1200 /tmp/scripts/apply-license.sh 2> /dev/null || timeout 1200 /tmp/scripts/apply-license.sh 2> /dev/null + {{- if .Values.global.tls.enabled }} + volumeMounts: + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" + {{- if .Values.global.acls.manageSystemACLs }} + initContainers: + - name: ent-license-acl-init + image: {{ .Values.global.imageK8S }} + {{ template "consul.imagePullPolicy" . }} + command: + - "/bin/sh" + - "-ec" + - | + exec consul-k8s-control-plane acl-init \ + -secret-name="{{ template "consul.fullname" . }}-enterprise-license-acl-token" \ + -k8s-namespace={{ .Release.Namespace }} \ + -consul-api-timeout={{ .Values.global.consulAPITimeout }} + resources: + requests: + memory: "25Mi" + cpu: "50m" + limits: + memory: "25Mi" + cpu: "50m" + {{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/enterprise-license-podsecuritypolicy.yaml b/charts/hashicorp/consul/1.5.3/templates/enterprise-license-podsecuritypolicy.yaml new file mode 100644 index 0000000000..cf96367473 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/enterprise-license-podsecuritypolicy.yaml @@ -0,0 +1,39 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey (not .Values.global.enterpriseLicense.enableLicenseAutoload)) }} +{{- if .Values.global.enablePodSecurityPolicies }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-enterprise-license + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: license +spec: + privileged: false + # Allow core volume types. + volumes: + - 'secret' + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/enterprise-license-role.yaml b/charts/hashicorp/consul/1.5.3/templates/enterprise-license-role.yaml new file mode 100644 index 0000000000..6a1b7fdffa --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/enterprise-license-role.yaml @@ -0,0 +1,37 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey (not .Values.global.enterpriseLicense.enableLicenseAutoload)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" . }}-enterprise-license + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: license +{{- if or .Values.global.acls.manageSystemACLs .Values.global.enablePodSecurityPolicies }} +rules: +{{- if .Values.global.acls.manageSystemACLs }} + - apiGroups: [""] + resources: + - secrets + resourceNames: + - {{ template "consul.fullname" . }}-enterprise-license-acl-token + verbs: + - get +{{- end }} +{{- if .Values.global.enablePodSecurityPolicies }} + - apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + resourceNames: + - {{ template "consul.fullname" . }}-enterprise-license + verbs: + - use +{{- end }} +{{- else }} +rules: [] +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/enterprise-license-rolebinding.yaml b/charts/hashicorp/consul/1.5.3/templates/enterprise-license-rolebinding.yaml new file mode 100644 index 0000000000..a21118b431 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/enterprise-license-rolebinding.yaml @@ -0,0 +1,22 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey (not .Values.global.enterpriseLicense.enableLicenseAutoload)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" . }}-enterprise-license + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: license +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" . }}-enterprise-license +subjects: + - kind: ServiceAccount + name: {{ template "consul.fullname" . }}-enterprise-license +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/enterprise-license-serviceaccount.yaml b/charts/hashicorp/consul/1.5.3/templates/enterprise-license-serviceaccount.yaml new file mode 100644 index 0000000000..31c9da841e --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/enterprise-license-serviceaccount.yaml @@ -0,0 +1,21 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey (not .Values.global.enterpriseLicense.enableLicenseAutoload)) }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-enterprise-license + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: license +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/expose-servers-service.yaml b/charts/hashicorp/consul/1.5.3/templates/expose-servers-service.yaml new file mode 100644 index 0000000000..d86cec9042 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/expose-servers-service.yaml @@ -0,0 +1,63 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- $serverExposeServiceEnabled := (or (and (ne (.Values.server.exposeService.enabled | toString) "-") .Values.server.exposeService.enabled) (and (eq (.Values.server.exposeService.enabled | toString) "-") .Values.global.adminPartitions.enabled)) -}} +{{- if (and $serverEnabled $serverExposeServiceEnabled) }} + +# Service with an external IP to reach Consul servers. +# Used for exposing gRPC port for peering and ports for client partitions to discover servers. +apiVersion: v1 +kind: Service +metadata: + name: {{ template "consul.fullname" . }}-expose-servers + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server + annotations: + {{- if .Values.server.exposeService.annotations }} + {{ tpl .Values.server.exposeService.annotations . | nindent 4 | trim }} + {{- end }} +spec: + type: "{{ .Values.server.exposeService.type }}" + ports: + {{- if (or (not .Values.global.tls.enabled) (not .Values.global.tls.httpsOnly)) }} + - name: http + port: 8500 + targetPort: 8500 + {{ if (and (eq .Values.server.exposeService.type "NodePort") .Values.server.exposeService.nodePort.http) }} + nodePort: {{ .Values.server.exposeService.nodePort.http }} + {{- end }} + {{- end }} + {{- if .Values.global.tls.enabled }} + - name: https + port: 8501 + targetPort: 8501 + {{ if (and (eq .Values.server.exposeService.type "NodePort") .Values.server.exposeService.nodePort.https) }} + nodePort: {{ .Values.server.exposeService.nodePort.https }} + {{- end }} + {{- end }} + - name: serflan + port: 8301 + targetPort: 8301 + {{ if (and (eq .Values.server.exposeService.type "NodePort") .Values.server.exposeService.nodePort.serf) }} + nodePort: {{ .Values.server.exposeService.nodePort.serf }} + {{- end }} + - name: rpc + port: 8300 + targetPort: 8300 + {{ if (and (eq .Values.server.exposeService.type "NodePort") .Values.server.exposeService.nodePort.rpc) }} + nodePort: {{ .Values.server.exposeService.nodePort.rpc }} + {{- end }} + - name: grpc + port: 8502 + targetPort: 8502 + {{ if (and (eq .Values.server.exposeService.type "NodePort") .Values.server.exposeService.nodePort.grpc) }} + nodePort: {{ .Values.server.exposeService.nodePort.grpc }} + {{- end }} + selector: + app: {{ template "consul.name" . }} + release: "{{ .Release.Name }}" + component: server +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/gateway-cleanup-clusterrole.yaml b/charts/hashicorp/consul/1.5.3/templates/gateway-cleanup-clusterrole.yaml new file mode 100644 index 0000000000..5518bfc390 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/gateway-cleanup-clusterrole.yaml @@ -0,0 +1,44 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "consul.fullname" . }}-gateway-cleanup + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gateway-cleanup +rules: + - apiGroups: + - consul.hashicorp.com + resources: + - gatewayclassconfigs + verbs: + - get + - delete + - apiGroups: + - gateway.networking.k8s.io + resources: + - gatewayclasses + verbs: + - get + - delete + - apiGroups: + - mesh.consul.hashicorp.com + resources: + - gatewayclassconfigs + - gatewayclasses + - meshgateways + verbs: + - get + - delete +{{- if .Values.global.enablePodSecurityPolicies }} + - apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + resourceNames: + - {{ template "consul.fullname" . }}-gateway-cleanup + verbs: + - use +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/gateway-cleanup-clusterrolebinding.yaml b/charts/hashicorp/consul/1.5.3/templates/gateway-cleanup-clusterrolebinding.yaml new file mode 100644 index 0000000000..9235f32101 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/gateway-cleanup-clusterrolebinding.yaml @@ -0,0 +1,20 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "consul.fullname" . }}-gateway-cleanup + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gateway-cleanup +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "consul.fullname" . }}-gateway-cleanup +subjects: + - kind: ServiceAccount + name: {{ template "consul.fullname" . }}-gateway-cleanup + namespace: {{ .Release.Namespace }} +{{- end }} \ No newline at end of file diff --git a/charts/hashicorp/consul/1.5.3/templates/gateway-cleanup-job.yaml b/charts/hashicorp/consul/1.5.3/templates/gateway-cleanup-job.yaml new file mode 100644 index 0000000000..0d38f6ec8b --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/gateway-cleanup-job.yaml @@ -0,0 +1,68 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "consul.fullname" . }}-gateway-cleanup + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gateway-cleanup + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} + annotations: + "helm.sh/hook": pre-delete + "helm.sh/hook-weight": "0" + "helm.sh/hook-delete-policy": hook-succeeded,hook-failed +spec: + template: + metadata: + name: {{ template "consul.fullname" . }}-gateway-cleanup + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: gateway-cleanup + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} + annotations: + "consul.hashicorp.com/connect-inject": "false" + "consul.hashicorp.com/mesh-inject": "false" + spec: + restartPolicy: Never + serviceAccountName: {{ template "consul.fullname" . }}-gateway-cleanup + containers: + - name: gateway-cleanup + image: {{ .Values.global.imageK8S }} + {{ template "consul.imagePullPolicy" . }} + {{- include "consul.restrictedSecurityContext" . | nindent 10 }} + command: + - consul-k8s-control-plane + args: + - gateway-cleanup + - -gateway-class-name=consul + - -gateway-class-config-name=consul-api-gateway + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" + volumeMounts: + - name: config + mountPath: /consul/config + readOnly: true + {{- if .Values.global.acls.tolerations }} + tolerations: + {{ tpl .Values.global.acls.tolerations . | indent 8 | trim }} + {{- end }} + volumes: + - name: config + configMap: + name: {{ template "consul.fullname" . }}-gateway-resources-config +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/gateway-cleanup-podsecuritypolicy.yaml b/charts/hashicorp/consul/1.5.3/templates/gateway-cleanup-podsecuritypolicy.yaml new file mode 100644 index 0000000000..ffbad130cc --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/gateway-cleanup-podsecuritypolicy.yaml @@ -0,0 +1,32 @@ +{{- if (and .Values.connectInject.enabled .Values.global.enablePodSecurityPolicies)}} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-gateway-cleanup + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gateway-cleanup +spec: + privileged: false + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/gateway-cleanup-serviceaccount.yaml b/charts/hashicorp/consul/1.5.3/templates/gateway-cleanup-serviceaccount.yaml new file mode 100644 index 0000000000..52c340f69d --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/gateway-cleanup-serviceaccount.yaml @@ -0,0 +1,19 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-gateway-cleanup + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gateway-cleanup +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/gateway-resources-clusterrole.yaml b/charts/hashicorp/consul/1.5.3/templates/gateway-resources-clusterrole.yaml new file mode 100644 index 0000000000..ad7082f060 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/gateway-resources-clusterrole.yaml @@ -0,0 +1,47 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "consul.fullname" . }}-gateway-resources + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gateway-resources +rules: + - apiGroups: + - mesh.consul.hashicorp.com + resources: + - meshgateways + verbs: + - get + - update + - create + - apiGroups: + - consul.hashicorp.com + - mesh.consul.hashicorp.com + resources: + - gatewayclassconfigs + verbs: + - get + - update + - create + - apiGroups: + - gateway.networking.k8s.io + - mesh.consul.hashicorp.com + resources: + - gatewayclasses + verbs: + - get + - update + - create +{{- if .Values.global.enablePodSecurityPolicies }} + - apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + resourceNames: + - {{ template "consul.fullname" . }}-gateway-resources + verbs: + - use +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/gateway-resources-clusterrolebinding.yaml b/charts/hashicorp/consul/1.5.3/templates/gateway-resources-clusterrolebinding.yaml new file mode 100644 index 0000000000..921df23239 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/gateway-resources-clusterrolebinding.yaml @@ -0,0 +1,20 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "consul.fullname" . }}-gateway-resources + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gateway-resources +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "consul.fullname" . }}-gateway-resources +subjects: + - kind: ServiceAccount + name: {{ template "consul.fullname" . }}-gateway-resources + namespace: {{ .Release.Namespace }} +{{- end }} \ No newline at end of file diff --git a/charts/hashicorp/consul/1.5.3/templates/gateway-resources-configmap.yaml b/charts/hashicorp/consul/1.5.3/templates/gateway-resources-configmap.yaml new file mode 100644 index 0000000000..d00f9b3e86 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/gateway-resources-configmap.yaml @@ -0,0 +1,195 @@ +{{- if .Values.connectInject.enabled }} + +# Validation +# For meshGateway.wanAddress, static must be set if source is "Static" +{{if (and (eq .Values.meshGateway.wanAddress.source "Static") (eq .Values.meshGateway.wanAddress.static ""))}}{{fail ".meshGateway.wanAddress.static must be set to a value if .meshGateway.wanAddress.source is Static"}}{{ end }} + +# Configuration of Gateway Resources Job which creates managed Gateway configuration. +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "consul.fullname" . }}-gateway-resources-config + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gateway-resources +data: + {{- if .Values.connectInject.apiGateway.managedGatewayClass.resources }} + resources.json: | + {{ toJson .Values.connectInject.apiGateway.managedGatewayClass.resources }} + {{- end }} + {{- if and (mustHas "resource-apis" .Values.global.experiments) (or .Values.meshGateway.enabled .Values.connectInject.apiGateway.managedGatewayClass) }} + config.yaml: | + gatewayClassConfigs: + {{- if .Values.meshGateway.enabled }} + - apiVersion: mesh.consul.hashicorp.com/v2beta1 + metadata: + name: consul-mesh-gateway + kind: GatewayClassConfig + spec: + labels: + set: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: mesh-gateway + deployment: + {{- if .Values.meshGateway.priorityClassName }} + priorityClassName: {{ .Values.meshGateway.priorityClassName | quote }} + {{- end }} + {{- if .Values.meshGateway.affinity }} + affinity: {{ toJson (default "{}" .Values.meshGateway.affinity) }} + {{- end }} + {{- if .Values.meshGateway.annotations }} + annotations: + set: {{ toJson .Values.meshGateway.annotations }} + {{- end }} + {{- if .Values.global.extraLabels }} + labels: + set: {{ toJson .Values.global.extraLabels }} + {{- end }} + container: + consul: + logging: + level: {{ default .Values.global.logLevel .Values.meshGateway.logLevel }} + portModifier: {{ sub .Values.meshGateway.containerPort .Values.meshGateway.service.port }} + {{- if .Values.meshGateway.hostPort }} + hostPort: {{ .Values.meshGateway.hostPort }} + {{- end }} + resources: {{ toJson .Values.meshGateway.resources }} + initContainer: + consul: + logging: + level: {{ default .Values.global.logLevel .Values.meshGateway.logLevel }} + resources: {{ toJson .Values.meshGateway.initServiceInitContainer.resources }} + {{- with .Values.meshGateway.nodeSelector }} + nodeSelector: {{ fromYaml . | toJson }} + {{- end }} + {{- with .Values.meshGateway.hostNetwork }} + hostNetwork: {{ . }} + {{- end }} + {{- with .Values.meshGateway.dnsPolicy }} + dnsPolicy: {{ . }} + {{- end }} + {{- with .Values.meshGateway.topologySpreadConstraints }} + topologySpreadConstraints: + {{ fromYamlArray . | toJson }} + {{- end }} + {{- if .Values.meshGateway.affinity }} + affinity: + {{ tpl .Values.meshGateway.affinity . | nindent 16 | trim }} + {{- end }} + replicas: + default: {{ .Values.meshGateway.replicas }} + min: {{ .Values.meshGateway.replicas }} + max: {{ .Values.meshGateway.replicas }} + {{- if .Values.meshGateway.tolerations }} + tolerations: + {{ fromYamlArray .Values.meshGateway.tolerations | toJson }} + {{- end }} + service: + {{- if .Values.meshGateway.service.annotations }} + annotations: + set: {{ toJson .Values.meshGateway.service.annotations }} + {{- end }} + type: {{ .Values.meshGateway.service.type }} + {{- if .Values.meshGateway.serviceAccount.annotations }} + serviceAccount: + annotations: + set: {{ toJson .Values.meshGateway.serviceAccount.annotations }} + {{- end }} + {{- end }} + {{- if .Values.connectInject.apiGateway.managedGatewayClass }} + - apiVersion: mesh.consul.hashicorp.com/v2beta1 + metadata: + name: consul-api-gateway + kind: GatewayClassConfig + spec: + labels: + set: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: api-gateway + {{- if .Values.connectInject.apiGateway.managedGatewayClass.copyAnnotations }} + {{- if .Values.connectInject.apiGateway.managedGatewayClass.copyAnnotations.service }} + annotations: + service: + {{ fromYamlArray .Values.connectInject.apiGateway.managedGatewayClass.copyAnnotations.service.annotations | toYaml }} + {{- end}} + {{- end}} + deployment: + {{- if .Values.connectInject.apiGateway.managedGatewayClass.nodeSelector }} + nodeSelector: + {{ fromYamlArray .Values.connectInject.apiGateway.managedGatewayClass.nodeSelector | toYaml }} + {{- end }} + initContainer: + {{- if .Values.connectInject.apiGateway.managedGatewayClass.mapPrivilegedContainerPorts }} + portModifier: {{ .Values.connectInject.apiGateway.managedGatewayClass.mapPrivilegedContainerPorts }} + {{- end }} + consul: + logging: + level: {{ .Values.global.logLevel }} + container: + {{- if .Values.connectInject.apiGateway.managedGatewayClass.mapPrivilegedContainerPorts }} + portModifier: {{ .Values.connectInject.apiGateway.managedGatewayClass.mapPrivilegedContainerPorts }} + {{- end }} + consul: + logging: + level: {{ .Values.global.logLevel }} + replicas: + default: {{ .Values.connectInject.apiGateway.managedGatewayClass.deployment.defaultInstances }} + min: {{ .Values.connectInject.apiGateway.managedGatewayClass.deployment.minInstances }} + max: {{ .Values.connectInject.apiGateway.managedGatewayClass.deployment.maxInstances }} + {{- if .Values.connectInject.apiGateway.managedGatewayClass.tolerations }} + tolerations: + {{ fromYamlArray .Values.connectInject.apiGateway.managedGatewayClass.tolerations | toYaml }} + {{- end }} + {{- if .Values.connectInject.apiGateway.managedGatewayClass.service }} + service: + annotations: + set: {{ toYaml .Values.connectInject.apiGateway.managedGatewayClass.service.annotations }} + {{- end }} + type: {{ .Values.connectInject.apiGateway.managedGatewayClass.serviceType }} + {{- if .Values.connectInject.apiGateway.managedGatewayClass.serviceAccount }} + serviceAccount: + annotations: + set: {{ toYaml .Values.connectInject.apiGateway.managedGatewayClass.serviceAccount.annotations }} + {{- end }} + {{- end }} + {{- if .Values.meshGateway.enabled }} + meshGateways: + - apiVersion: mesh.consul.hashicorp.com/v2beta1 + kind: MeshGateway + metadata: + name: mesh-gateway + namespace: {{ .Release.Namespace }} + annotations: + "consul.hashicorp.com/gateway-wan-address-source": {{ .Values.meshGateway.wanAddress.source | quote }} + "consul.hashicorp.com/gateway-wan-address-static": {{ .Values.meshGateway.wanAddress.static | quote }} + {{- if eq .Values.meshGateway.wanAddress.source "Service" }} + {{- if eq .Values.meshGateway.service.type "NodePort" }} + "consul.hashicorp.com/gateway-wan-port": {{ .Values.meshGateway.service.nodePort | quote }} + {{- else }} + "consul.hashicorp.com/gateway-wan-port": {{ .Values.meshGateway.service.port | quote }} + {{- end }} + {{- else }} + "consul.hashicorp.com/gateway-wan-port": {{ .Values.meshGateway.wanAddress.port | quote }} + {{- end }} + spec: + gatewayClassName: consul-mesh-gateway + listeners: + - name: "wan" + port: {{ .Values.meshGateway.service.port }} + protocol: "TCP" + workloads: + prefixes: + - "mesh-gateway" + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/gateway-resources-job.yaml b/charts/hashicorp/consul/1.5.3/templates/gateway-resources-job.yaml new file mode 100644 index 0000000000..5f3110479c --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/gateway-resources-job.yaml @@ -0,0 +1,108 @@ +{{- if .Values.apiGateway}}{{fail "[DEPRECATED and REMOVED] the apiGateway stanza is no longer supported as of Consul 1.19.0. Use connectInject.apiGateway instead."}}{{- end -}} +{{- if .Values.connectInject.enabled }} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "consul.fullname" . }}-gateway-resources + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gateway-resources + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-weight": "0" + "helm.sh/hook-delete-policy": hook-succeeded +spec: + template: + metadata: + name: {{ template "consul.fullname" . }}-gateway-resources + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: gateway-resources + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} + annotations: + "consul.hashicorp.com/connect-inject": "false" + "consul.hashicorp.com/mesh-inject": "false" + spec: + restartPolicy: Never + serviceAccountName: {{ template "consul.fullname" . }}-gateway-resources + containers: + - name: gateway-resources + image: {{ .Values.global.imageK8S }} + {{ template "consul.imagePullPolicy" . }} + {{- include "consul.restrictedSecurityContext" . | nindent 10 }} + command: + - consul-k8s-control-plane + args: + - gateway-resources + - -gateway-class-name=consul + - -gateway-class-config-name=consul-api-gateway + - -controller-name=consul.hashicorp.com/gateway-controller + - -app={{template "consul.name" .}} + - -chart={{template "consul.chart" .}} + - -heritage={{ .Release.Service }} + - -release-name={{ .Release.Name }} + - -component=api-gateway + {{- if .Values.connectInject.apiGateway.managedGatewayClass.deployment }} + {{- if .Values.connectInject.apiGateway.managedGatewayClass.deployment.defaultInstances }} + - -deployment-default-instances={{ .Values.connectInject.apiGateway.managedGatewayClass.deployment.defaultInstances }} + {{- end}} + {{- if .Values.connectInject.apiGateway.managedGatewayClass.deployment.maxInstances }} + - -deployment-max-instances={{ .Values.connectInject.apiGateway.managedGatewayClass.deployment.maxInstances }} + {{- end}} + {{- if .Values.connectInject.apiGateway.managedGatewayClass.deployment.minInstances }} + - -deployment-min-instances={{ .Values.connectInject.apiGateway.managedGatewayClass.deployment.minInstances }} + {{- end}} + {{- end}} + {{- if .Values.connectInject.apiGateway.managedGatewayClass.nodeSelector }} + - -node-selector + - {{- toYaml .Values.connectInject.apiGateway.managedGatewayClass.nodeSelector | nindent 14 -}} + {{- end }} + {{- if .Values.connectInject.apiGateway.managedGatewayClass.tolerations }} + - -tolerations={{ .Values.connectInject.apiGateway.managedGatewayClass.tolerations }} + {{- end }} + {{- if .Values.connectInject.apiGateway.managedGatewayClass.copyAnnotations.service }} + - -service-annotations + - {{- toYaml .Values.connectInject.apiGateway.managedGatewayClass.copyAnnotations.service.annotations | nindent 14 -}} + {{- end }} + - -service-type={{ .Values.connectInject.apiGateway.managedGatewayClass.serviceType }} + {{- if .Values.global.openshift.enabled }} + - -openshift-scc-name={{ .Values.connectInject.apiGateway.managedGatewayClass.openshiftSCCName }} + {{- end }} + - -map-privileged-container-ports={{ .Values.connectInject.apiGateway.managedGatewayClass.mapPrivilegedContainerPorts }} + {{- if (ne (.Values.connectInject.apiGateway.managedGatewayClass.metrics.enabled | toString) "-") }} + - -enable-metrics={{ .Values.connectInject.apiGateway.managedGatewayClass.metrics.enabled | toString }} + {{- end }} + {{- if .Values.connectInject.apiGateway.managedGatewayClass.metrics.path }} + - -metrics-path={{ .Values.connectInject.apiGateway.managedGatewayClass.metrics.path }} + {{- end }} + {{- if .Values.connectInject.apiGateway.managedGatewayClass.metrics.port }} + - -metrics-port={{ .Values.connectInject.apiGateway.managedGatewayClass.metrics.port }} + {{- end }} + {{- with .Values.connectInject.apiGateway.managedGatewayClass.resourceJob.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + - name: config + mountPath: /consul/config + readOnly: true + {{- if .Values.global.acls.tolerations }} + tolerations: + {{ tpl .Values.global.acls.tolerations . | indent 8 | trim }} + {{- end }} + volumes: + - name: config + configMap: + name: {{ template "consul.fullname" . }}-gateway-resources-config +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/gateway-resources-podsecuritypolicy.yaml b/charts/hashicorp/consul/1.5.3/templates/gateway-resources-podsecuritypolicy.yaml new file mode 100644 index 0000000000..da5299194c --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/gateway-resources-podsecuritypolicy.yaml @@ -0,0 +1,32 @@ +{{- if (and .Values.global.enablePodSecurityPolicies .Values.connectInject.enabled)}} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-gateway-resources + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gateway-resources +spec: + privileged: false + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/gateway-resources-serviceaccount.yaml b/charts/hashicorp/consul/1.5.3/templates/gateway-resources-serviceaccount.yaml new file mode 100644 index 0000000000..db3a44984f --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/gateway-resources-serviceaccount.yaml @@ -0,0 +1,19 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-gateway-resources + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gateway-resources +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/gossip-encryption-autogenerate-job.yaml b/charts/hashicorp/consul/1.5.3/templates/gossip-encryption-autogenerate-job.yaml new file mode 100644 index 0000000000..485064f80f --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/gossip-encryption-autogenerate-job.yaml @@ -0,0 +1,71 @@ +{{- if .Values.global.gossipEncryption.autoGenerate }} +{{- if (or .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey) }} + {{ fail "If global.gossipEncryption.autoGenerate is true, global.gossipEncryption.secretName and global.gossipEncryption.secretKey must not be set." }} +{{ end }} +# automatically generate encryption key for gossip protocol and save it in Kubernetes secret +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "consul.fullname" . }}-gossip-encryption-autogenerate + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gossip-encryption-autogenerate + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "1" + "helm.sh/hook-delete-policy": hook-succeeded,before-hook-creation +spec: + template: + metadata: + name: {{ template "consul.fullname" . }}-gossip-encryption-autogenerate + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: gossip-encryption-autogenerate + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} + annotations: + "consul.hashicorp.com/connect-inject": "false" + "consul.hashicorp.com/mesh-inject": "false" + spec: + restartPolicy: Never + serviceAccountName: {{ template "consul.fullname" . }}-gossip-encryption-autogenerate + {{- if not .Values.global.openshift.enabled }} + securityContext: + runAsNonRoot: true + runAsGroup: 1000 + runAsUser: 100 + fsGroup: 1000 + {{- end }} + containers: + - name: gossip-encryption-autogen + image: "{{ .Values.global.imageK8S }}" + {{ template "consul.imagePullPolicy" . }} + {{- include "consul.restrictedSecurityContext" . | nindent 10 }} + command: + - "/bin/sh" + - "-ec" + - | + exec consul-k8s-control-plane gossip-encryption-autogenerate \ + -namespace={{ .Release.Namespace }} \ + -secret-name={{ template "consul.fullname" . }}-gossip-encryption-key \ + -secret-key="key" \ + -log-level={{ default .Values.global.logLevel .Values.global.gossipEncryption.logLevel }} \ + -log-json={{ .Values.global.logJSON }} + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/gossip-encryption-autogenerate-podsecuritypolicy.yaml b/charts/hashicorp/consul/1.5.3/templates/gossip-encryption-autogenerate-podsecuritypolicy.yaml new file mode 100644 index 0000000000..209b3aa343 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/gossip-encryption-autogenerate-podsecuritypolicy.yaml @@ -0,0 +1,40 @@ +{{- if and .Values.global.gossipEncryption.autoGenerate .Values.global.enablePodSecurityPolicies }} +--- +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-gossip-encryption-autogenerate + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gossip-encryption-autogenerate + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + # Allow core volume types. + volumes: + - 'secret' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/gossip-encryption-autogenerate-role.yaml b/charts/hashicorp/consul/1.5.3/templates/gossip-encryption-autogenerate-role.yaml new file mode 100644 index 0000000000..8c51c96ffe --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/gossip-encryption-autogenerate-role.yaml @@ -0,0 +1,32 @@ +{{- if .Values.global.gossipEncryption.autoGenerate }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" . }}-gossip-encryption-autogenerate + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gossip-encryption-autogenerate + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation +rules: +- apiGroups: [""] + resources: + - secrets + verbs: + - create + - get +{{- if .Values.global.enablePodSecurityPolicies }} +- apiGroups: ["policy"] + resources: + - podsecuritypolicies + verbs: + - use + resourceNames: + - {{ template "consul.fullname" . }}-gossip-encryption-autogenerate +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/gossip-encryption-autogenerate-rolebinding.yaml b/charts/hashicorp/consul/1.5.3/templates/gossip-encryption-autogenerate-rolebinding.yaml new file mode 100644 index 0000000000..7118475f64 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/gossip-encryption-autogenerate-rolebinding.yaml @@ -0,0 +1,23 @@ +{{- if .Values.global.gossipEncryption.autoGenerate }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" . }}-gossip-encryption-autogenerate + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gossip-encryption-autogenerate + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" . }}-gossip-encryption-autogenerate +subjects: +- kind: ServiceAccount + name: {{ template "consul.fullname" . }}-gossip-encryption-autogenerate +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/gossip-encryption-autogenerate-serviceaccount.yaml b/charts/hashicorp/consul/1.5.3/templates/gossip-encryption-autogenerate-serviceaccount.yaml new file mode 100644 index 0000000000..1fd620237f --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/gossip-encryption-autogenerate-serviceaccount.yaml @@ -0,0 +1,22 @@ +{{- if .Values.global.gossipEncryption.autoGenerate }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-gossip-encryption-autogenerate + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gossip-encryption-autogenerate + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/ingress-gateways-deployment.yaml b/charts/hashicorp/consul/1.5.3/templates/ingress-gateways-deployment.yaml new file mode 100644 index 0000000000..c7a38bb040 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/ingress-gateways-deployment.yaml @@ -0,0 +1,379 @@ +{{- if .Values.ingressGateways.enabled }} +{{- if not .Values.connectInject.enabled }}{{ fail "connectInject.enabled must be true" }}{{ end -}} +{{- if and .Values.global.adminPartitions.enabled (not .Values.global.enableConsulNamespaces) }}{{ fail "global.enableConsulNamespaces must be true if global.adminPartitions.enabled=true" }}{{ end }} +{{- if .Values.global.lifecycleSidecarContainer }}{{ fail "global.lifecycleSidecarContainer has been renamed to global.consulSidecarContainer. Please set values using global.consulSidecarContainer." }}{{ end }} +{{ template "consul.validateRequiredCloudSecretsExist" . }} +{{ template "consul.validateCloudSecretKeys" . }} + +{{- $root := . }} +{{- $defaults := .Values.ingressGateways.defaults }} +{{- $names := dict }} + +{{- /* Check if gateway names are unique. */ -}} +{{- $gateways := .Values.ingressGateways.gateways }} +{{- range $outerIngressIndex, $outerIngressVal := $gateways }} + +{{- range $innerIngressIndex, $innerIngressVal := $gateways }} +{{- if (and (ne $outerIngressIndex $innerIngressIndex) (eq $outerIngressVal.name $innerIngressVal.name)) }} +{{ fail (cat "ingress gateways must have unique names but found duplicate name" $innerIngressVal.name) }} +{{ end -}} +{{ end -}} +{{ end -}} + +{{- range .Values.ingressGateways.gateways }} + +{{- $service := .service }} + +{{- if empty .name }} +# Check that the gateway name is provided +{{ fail "Ingress gateway names cannot be empty"}} +{{ end -}} +{{- if hasKey $names .name }} +# Check that the gateway name is unique +{{ fail "Ingress gateway names must be unique"}} +{{ end -}} +{{- /* Add the gateway name to the $names dict to ensure uniqueness */ -}} +{{- $_ := set $names .name .name }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "consul.fullname" $root }}-{{ .name }} + namespace: {{ $root.Release.Namespace }} + labels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: ingress-gateway + ingress-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} + {{- if $root.Values.global.extraLabels }} + {{- toYaml $root.Values.global.extraLabels | nindent 4 }} + {{- end }} +spec: + replicas: {{ default $defaults.replicas .replicas }} + selector: + matchLabels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: ingress-gateway + ingress-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} + template: + metadata: + labels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: ingress-gateway + ingress-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} + consul.hashicorp.com/connect-inject-managed-by: consul-k8s-endpoints-controller + {{- if $root.Values.global.extraLabels }} + {{- toYaml $root.Values.global.extraLabels | nindent 8 }} + {{- end }} + annotations: + "consul.hashicorp.com/connect-inject": "false" + "consul.hashicorp.com/mesh-inject": "false" + "consul.hashicorp.com/gateway-kind": "ingress-gateway" + "consul.hashicorp.com/gateway-consul-service-name": "{{ .name }}" + {{- if $root.Values.global.enableConsulNamespaces }} + "consul.hashicorp.com/gateway-namespace": {{ (default $defaults.consulNamespace .consulNamespace) }} + {{- end }} + "consul.hashicorp.com/gateway-wan-address-source": "Service" + {{- $serviceType := (default $defaults.service.type $service.type) }} + {{- if (eq $serviceType "NodePort") }} + {{- if $service.ports }} + {{- $firstPort := first $service.ports}} + {{- if $firstPort.nodePort }} + "consul.hashicorp.com/gateway-wan-port": "{{ $firstPort.nodePort }}" + {{- else }}{{ fail "if ingressGateways .service.type=NodePort and defining ingressGateways.gateways.service.ports, the first port entry must include a nodePort" }} + {{- end }} + {{- else if $defaults.service.ports }} + {{- $firstDefaultPort := first $defaults.service.ports}} + {{- if $firstDefaultPort.nodePort }} + "consul.hashicorp.com/gateway-wan-port": "{{ $firstDefaultPort.nodePort }}" + {{- else }}{{ fail "if ingressGateways .service.type=NodePort and using ingressGateways.defaults.service.ports, the first port entry must include a nodePort" }} + {{- end }} + {{- else }}{{ fail "if ingressGateways .service.type=NodePort, the first port entry in either the defaults or specific gateway must include a nodePort" }} + {{- end }} + {{- else }} + {{- if $service.ports }} + {{- $firstPort := first $service.ports}} + {{- if $firstPort.port }} + "consul.hashicorp.com/gateway-wan-port": "{{ $firstPort.port }}" + {{- else }}{{ fail "if ingressGateways .service.type is not NodePort and defining ingressGateways.gateways.service.ports, the first port entry must include a port" }} + {{- end }} + {{- else if $defaults.service.ports }} + {{- $firstDefaultPort := first $defaults.service.ports}} + {{- if $firstDefaultPort.port }} + "consul.hashicorp.com/gateway-wan-port": "{{ $firstDefaultPort.port }}" + {{- else }}{{ fail "if ingressGateways .service.type is not NodePort and using ingressGateways.defaults.service.ports, the first port entry must include a port" }} + {{- end }} + {{- else }}{{ fail "if ingressGateways .service.type is not NodePort, the first port entry in either the defaults or specific gateway must include a port" }} + {{- end }} + {{- end }} + {{- if (and $root.Values.global.secretsBackend.vault.enabled $root.Values.global.tls.enabled) }} + "vault.hashicorp.com/agent-init-first": "true" + "vault.hashicorp.com/agent-inject": "true" + "vault.hashicorp.com/role": {{ $root.Values.global.secretsBackend.vault.consulCARole }} + "vault.hashicorp.com/agent-inject-secret-serverca.crt": {{ $root.Values.global.tls.caCert.secretName }} + "vault.hashicorp.com/agent-inject-template-serverca.crt": {{ template "consul.serverTLSCATemplate" $root }} + {{- if and $root.Values.global.secretsBackend.vault.ca.secretName $root.Values.global.secretsBackend.vault.ca.secretKey }} + "vault.hashicorp.com/agent-extra-secret": {{ $root.Values.global.secretsBackend.vault.ca.secretName }} + "vault.hashicorp.com/ca-cert": /vault/custom/{{ $root.Values.global.secretsBackend.vault.ca.secretKey }} + {{- end }} + {{- if $root.Values.global.secretsBackend.vault.agentAnnotations }} + {{ tpl $root.Values.global.secretsBackend.vault.agentAnnotations $root | nindent 8 | trim }} + {{- end }} + {{- if (and ($root.Values.global.secretsBackend.vault.vaultNamespace) (not (hasKey (default "" $root.Values.global.secretsBackend.vault.agentAnnotations | fromYaml) "vault.hashicorp.com/namespace")))}} + "vault.hashicorp.com/namespace": "{{ $root.Values.global.secretsBackend.vault.vaultNamespace }}" + {{- end }} + {{- end }} + {{- if (and $root.Values.global.metrics.enabled $root.Values.global.metrics.enableGatewayMetrics) }} + "prometheus.io/scrape": "true" + {{- if not (hasKey (default "" $defaults.annotations | fromYaml) "prometheus.io/path")}} + "prometheus.io/path": "/metrics" + {{- end }} + "prometheus.io/port": "20200" + {{- end }} + {{- if $defaults.annotations }} + # We allow both default annotations and gateway-specific annotations + {{- tpl $defaults.annotations $root | nindent 8 }} + {{- end }} + {{- if .annotations }} + {{- tpl .annotations $root | nindent 8 }} + {{- end }} + spec: + {{- if (or $defaults.affinity .affinity) }} + affinity: + {{ tpl (default $defaults.affinity .affinity) $root | nindent 8 | trim }} + {{- end }} + {{- if (or $defaults.tolerations .tolerations) }} + tolerations: + {{ tpl (default $defaults.tolerations .tolerations) $root | nindent 8 | trim }} + {{- end }} + {{- if (or $defaults.topologySpreadConstraints .topologySpreadConstraints) }} + topologySpreadConstraints: + {{ tpl (default $defaults.topologySpreadConstraints .topologySpreadConstraints) $root | nindent 8 | trim }} + {{- end }} + terminationGracePeriodSeconds: {{ default $defaults.terminationGracePeriodSeconds .terminationGracePeriodSeconds }} + serviceAccountName: {{ template "consul.fullname" $root }}-{{ .name }} + volumes: + - name: tmp + emptyDir: + medium: "Memory" + - name: consul-service + emptyDir: + medium: "Memory" + {{- if $root.Values.global.tls.enabled }} + {{- if not (or (and $root.Values.externalServers.enabled $root.Values.externalServers.useSystemRoots) ($root.Values.global.secretsBackend.vault.enabled)) }} + - name: consul-ca-cert + secret: + {{- if $root.Values.global.tls.caCert.secretName }} + secretName: {{ $root.Values.global.tls.caCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" $root }}-ca-cert + {{- end }} + items: + - key: {{ default "tls.crt" $root.Values.global.tls.caCert.secretKey }} + path: tls.crt + {{- end }} + {{- end }} + initContainers: + # ingress-gateway-init registers the ingress gateway service with Consul. + - name: ingress-gateway-init + image: {{ $root.Values.global.imageK8S }} + {{ template "consul.imagePullPolicy" $root }} + {{- include "consul.restrictedSecurityContext" $ | nindent 8 }} + env: + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + {{- include "consul.consulK8sConsulServerEnvVars" $root | nindent 8 }} + {{- if $root.Values.global.enableConsulNamespaces }} + - name: CONSUL_NAMESPACE + value: {{ (default $defaults.consulNamespace .consulNamespace) }} + {{- end }} + {{- if $root.Values.global.acls.manageSystemACLs }} + - name: CONSUL_LOGIN_AUTH_METHOD + value: {{ template "consul.fullname" $root }}-k8s-component-auth-method + - name: CONSUL_LOGIN_DATACENTER + value: {{ $root.Values.global.datacenter }} + - name: CONSUL_LOGIN_META + value: "component=ingress-gateway,pod=$(NAMESPACE)/$(POD_NAME)" + {{- end }} + - name: CONSUL_NODE_NAME + value: $(NODE_NAME)-virtual + command: + - "/bin/sh" + - "-ec" + - | + exec consul-k8s-control-plane connect-init -pod-name=${POD_NAME} -pod-namespace=${NAMESPACE} \ + -gateway-kind="ingress-gateway" \ + -proxy-id-file=/consul/service/proxy-id \ + -service-name={{ template "consul.fullname" $root }}-{{ .name }} \ + -log-level={{ default $root.Values.global.logLevel $root.Values.ingressGateways.logLevel }} \ + -log-json={{ $root.Values.global.logJSON }} + volumeMounts: + - name: tmp + mountPath: /tmp + - name: consul-service + mountPath: /consul/service + {{- if $root.Values.global.tls.enabled }} + {{- if not (or (and $root.Values.externalServers.enabled $root.Values.externalServers.useSystemRoots) ($root.Values.global.secretsBackend.vault.enabled)) }} + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- end }} + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" + containers: + - name: ingress-gateway + image: {{ $root.Values.global.imageConsulDataplane | quote }} + {{ template "consul.imagePullPolicy" $root }} + {{- include "consul.restrictedSecurityContext" $ | nindent 8 }} + {{- if (default $defaults.resources .resources) }} + resources: {{ toYaml (default $defaults.resources .resources) | nindent 10 }} + {{- end }} + volumeMounts: + - name: tmp + mountPath: /tmp + - name: consul-service + mountPath: /consul/service + readOnly: true + {{- if $root.Values.global.tls.enabled }} + {{- if not (or (and $root.Values.externalServers.enabled $root.Values.externalServers.useSystemRoots) ($root.Values.global.secretsBackend.vault.enabled)) }} + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- end }} + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: DP_ENVOY_READY_BIND_ADDRESS + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: DP_CREDENTIAL_LOGIN_META1 + value: pod=$(NAMESPACE)/$(POD_NAME) + - name: DP_CREDENTIAL_LOGIN_META2 + value: component=ingress-gateway + - name: DP_SERVICE_NODE_NAME + value: $(NODE_NAME)-virtual + command: + - consul-dataplane + args: + - -envoy-ready-bind-port=21000 + {{- if $root.Values.externalServers.enabled }} + - -addresses={{ $root.Values.externalServers.hosts | first }} + {{- else }} + - -addresses={{ template "consul.fullname" $root }}-server.{{ $root.Release.Namespace }}.svc + {{- end }} + {{- if $root.Values.externalServers.enabled }} + - -grpc-port={{ $root.Values.externalServers.grpcPort }} + {{- else }} + - -grpc-port=8502 + {{- end }} + - -proxy-service-id-path=/consul/service/proxy-id + {{- if $root.Values.global.enableConsulNamespaces }} + - -service-namespace={{ (default $defaults.consulNamespace .consulNamespace) }} + {{- end }} + {{- if and $root.Values.global.tls.enabled }} + {{- if (not (and $root.Values.externalServers.enabled $root.Values.externalServers.useSystemRoots)) }} + {{- if $root.Values.global.secretsBackend.vault.enabled }} + - -ca-certs=/vault/secrets/serverca.crt + {{- else }} + - -ca-certs=/consul/tls/ca/tls.crt + {{- end }} + {{- end }} + {{- if and $root.Values.externalServers.enabled $root.Values.externalServers.tlsServerName }} + - -tls-server-name={{ $root.Values.externalServers.tlsServerName }} + {{- else if $root.Values.global.cloud.enabled }} + - -tls-server-name=server.{{ $root.Values.global.datacenter}}.{{ $root.Values.global.domain}} + {{- end }} + {{- else }} + - -tls-disabled + {{- end }} + {{- if $root.Values.global.acls.manageSystemACLs }} + - -credential-type=login + - -login-bearer-token-path=/var/run/secrets/kubernetes.io/serviceaccount/token + - -login-auth-method={{ template "consul.fullname" $root }}-k8s-component-auth-method + {{- if $root.Values.global.adminPartitions.enabled }} + - -login-partition={{ $root.Values.global.adminPartitions.name }} + {{- end }} + {{- end }} + {{- if $root.Values.global.adminPartitions.enabled }} + - -service-partition={{ $root.Values.global.adminPartitions.name }} + {{- end }} + - -log-level={{ default $root.Values.global.logLevel $root.Values.ingressGateways.logLevel }} + - -log-json={{ $root.Values.global.logJSON }} + {{- if (and $root.Values.global.metrics.enabled $root.Values.global.metrics.enableGatewayMetrics) }} + - -telemetry-prom-scrape-path=/metrics + {{- end }} + {{- if and $root.Values.externalServers.enabled $root.Values.externalServers.skipServerWatch }} + - -server-watch-disabled=true + {{- end }} + livenessProbe: + tcpSocket: + port: 21000 + failureThreshold: 3 + initialDelaySeconds: 30 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + readinessProbe: + tcpSocket: + port: 21000 + failureThreshold: 3 + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + ports: + - name: gateway-health + containerPort: 21000 + {{- range $index, $allPorts := (default $defaults.service.ports $service.ports) }} + - name: gateway-{{ $index }} + containerPort: {{ $allPorts.port }} + {{- end }} + {{- if (default $defaults.priorityClassName .priorityClassName) }} + priorityClassName: {{ default $defaults.priorityClassName .priorityClassName | quote }} + {{- end }} + {{- if (default $defaults.nodeSelector .nodeSelector) }} + nodeSelector: + {{ tpl (default $defaults.nodeSelector .nodeSelector) $root | indent 8 | trim }} + {{- end }} +--- +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/ingress-gateways-podsecuritypolicy.yaml b/charts/hashicorp/consul/1.5.3/templates/ingress-gateways-podsecuritypolicy.yaml new file mode 100644 index 0000000000..b847e44ebd --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/ingress-gateways-podsecuritypolicy.yaml @@ -0,0 +1,47 @@ +{{- if (and .Values.global.enablePodSecurityPolicies .Values.ingressGateways.enabled) }} +{{- $root := . }} +{{- range .Values.ingressGateways.gateways }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" $root }}-{{ .name }} + namespace: {{ $root.Release.Namespace }} + labels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: ingress-gateway + ingress-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + defaultAddCapabilities: + - NET_BIND_SERVICE + # Allow core volume types. + volumes: + - 'configMap' + - 'emptyDir' + - 'projected' + - 'secret' + - 'downwardAPI' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +--- +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/ingress-gateways-role.yaml b/charts/hashicorp/consul/1.5.3/templates/ingress-gateways-role.yaml new file mode 100644 index 0000000000..49e8486e58 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/ingress-gateways-role.yaml @@ -0,0 +1,46 @@ +{{- if .Values.ingressGateways.enabled }} + +{{- $root := . }} +{{- $defaults := .Values.ingressGateways.defaults }} + +{{- range .Values.ingressGateways.gateways }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" $root }}-{{ .name }} + namespace: {{ $root.Release.Namespace }} + labels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: ingress-gateway + ingress-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} +rules: + - apiGroups: [""] + resources: + - services + resourceNames: + - {{ template "consul.fullname" $root }}-{{ .name }} + verbs: + - get +{{- if $root.Values.global.enablePodSecurityPolicies }} + - apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + resourceNames: + - {{ template "consul.fullname" $root }}-{{ .name }} + verbs: + - use +{{- end }} +{{- if $root.Values.global.acls.manageSystemACLs }} + - apiGroups: [""] + resources: + - secrets + resourceNames: + - {{ template "consul.fullname" $root }}-{{ .name }}-acl-token + verbs: + - get +{{- end }} +--- +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/ingress-gateways-rolebinding.yaml b/charts/hashicorp/consul/1.5.3/templates/ingress-gateways-rolebinding.yaml new file mode 100644 index 0000000000..601de775f4 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/ingress-gateways-rolebinding.yaml @@ -0,0 +1,25 @@ +{{- if .Values.ingressGateways.enabled }} +{{- $root := . }} +{{- range .Values.ingressGateways.gateways }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" $root }}-{{ .name }} + namespace: {{ $root.Release.Namespace }} + labels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: ingress-gateway + ingress-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" $root }}-{{ .name }} +subjects: + - kind: ServiceAccount + name: {{ template "consul.fullname" $root }}-{{ .name }} +--- +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/ingress-gateways-service.yaml b/charts/hashicorp/consul/1.5.3/templates/ingress-gateways-service.yaml new file mode 100644 index 0000000000..cf54a740fe --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/ingress-gateways-service.yaml @@ -0,0 +1,51 @@ +{{- if .Values.ingressGateways.enabled }} + +{{- $root := . }} +{{- $defaults := .Values.ingressGateways.defaults }} + +{{- range .Values.ingressGateways.gateways }} + +{{- $service := .service }} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "consul.fullname" $root }}-{{ .name }} + namespace: {{ $root.Release.Namespace }} + labels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: ingress-gateway + ingress-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} + {{- if (or $defaults.service.annotations $service.annotations) }} + # We allow both default annotations and gateway-specific annotations + annotations: + {{- if $defaults.service.annotations }} + {{ tpl $defaults.service.annotations $root | nindent 4 | trim }} + {{- end }} + {{- if $service.annotations }} + {{ tpl $service.annotations $root | nindent 4 | trim }} + {{- end }} + {{- end }} +spec: + selector: + app: {{ template "consul.name" $root }} + release: "{{ $root.Release.Name }}" + component: ingress-gateway + ingress-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} + ports: + {{- range $index, $ports := (default $defaults.service.ports $service.ports) }} + - name: gateway-{{ $index }} + port: {{ $ports.port }} + {{- if (and (eq (default $defaults.service.type $service.type) "NodePort") $ports.nodePort) }} + nodePort: {{ $ports.nodePort }} + {{- end}} + {{- end }} + type: {{ default $defaults.service.type $service.type }} + {{- if (default $defaults.service.additionalSpec $service.additionalSpec) }} + {{ tpl (default $defaults.service.additionalSpec $service.additionalSpec) $root | nindent 2 | trim }} + {{- end }} +--- +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/ingress-gateways-serviceaccount.yaml b/charts/hashicorp/consul/1.5.3/templates/ingress-gateways-serviceaccount.yaml new file mode 100644 index 0000000000..cea6cafc21 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/ingress-gateways-serviceaccount.yaml @@ -0,0 +1,35 @@ +{{- if .Values.ingressGateways.enabled }} +{{- $root := . }} +{{- $defaults := .Values.ingressGateways.defaults }} +{{- range .Values.ingressGateways.gateways }} +{{- $serviceAccount := .serviceAccount }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" $root }}-{{ .name }} + namespace: {{ $root.Release.Namespace }} + labels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: ingress-gateway + ingress-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} + {{- if (or $defaults.serviceAccount.annotations $serviceAccount.annotations) }} + annotations: + {{- if $defaults.serviceAccount.annotations }} + {{ tpl $defaults.serviceAccount.annotations $root | nindent 4 | trim }} + {{- end }} + {{- if $serviceAccount.annotations }} + {{ tpl $serviceAccount.annotations $root | nindent 4 | trim }} + {{- end }} + {{- end }} +{{- with $root.Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +--- +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/mesh-gateway-clusterrole.yaml b/charts/hashicorp/consul/1.5.3/templates/mesh-gateway-clusterrole.yaml new file mode 100644 index 0000000000..3053105105 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/mesh-gateway-clusterrole.yaml @@ -0,0 +1,36 @@ +{{- if .Values.meshGateway.enabled }} +{{- if not (mustHas "resource-apis" .Values.global.experiments) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "consul.fullname" . }}-mesh-gateway + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: mesh-gateway +{{- if or .Values.global.acls.manageSystemACLs .Values.global.enablePodSecurityPolicies (eq .Values.meshGateway.wanAddress.source "Service") }} +rules: +{{- if .Values.global.enablePodSecurityPolicies }} + - apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + resourceNames: + - {{ template "consul.fullname" . }}-mesh-gateway + verbs: + - use +{{- end }} +{{- if eq .Values.meshGateway.wanAddress.source "Service" }} + - apiGroups: [""] + resources: + - services + resourceNames: + - {{ template "consul.fullname" . }}-mesh-gateway + verbs: + - get + {{- end }} +{{- else }} +rules: [] +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/mesh-gateway-clusterrolebinding.yaml b/charts/hashicorp/consul/1.5.3/templates/mesh-gateway-clusterrolebinding.yaml new file mode 100644 index 0000000000..2fb80fc04c --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/mesh-gateway-clusterrolebinding.yaml @@ -0,0 +1,22 @@ +{{- if .Values.meshGateway.enabled }} +{{- if not (mustHas "resource-apis" .Values.global.experiments) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "consul.fullname" . }}-mesh-gateway + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: mesh-gateway +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "consul.fullname" . }}-mesh-gateway +subjects: + - kind: ServiceAccount + name: {{ template "consul.fullname" . }}-mesh-gateway + namespace: {{ .Release.Namespace }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/mesh-gateway-deployment.yaml b/charts/hashicorp/consul/1.5.3/templates/mesh-gateway-deployment.yaml new file mode 100644 index 0000000000..03bf2707a5 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/mesh-gateway-deployment.yaml @@ -0,0 +1,325 @@ +{{- if .Values.meshGateway.enabled }} +{{- if not (mustHas "resource-apis" .Values.global.experiments) }} +{{- if not .Values.connectInject.enabled }}{{ fail "connectInject.enabled must be true" }}{{ end -}} +{{- if and .Values.global.acls.manageSystemACLs (ne .Values.meshGateway.consulServiceName "") (ne .Values.meshGateway.consulServiceName "mesh-gateway") }}{{ fail "if global.acls.manageSystemACLs is true, meshGateway.consulServiceName cannot be set" }}{{ end -}} +{{- if .Values.meshGateway.globalMode }}{{ fail "meshGateway.globalMode is no longer supported; instead, you must migrate to CRDs (see www.consul.io/docs/k8s/crds/upgrade-to-crds)" }}{{ end -}} +{{- if and .Values.global.adminPartitions.enabled (not .Values.global.enableConsulNamespaces) }}{{ fail "global.enableConsulNamespaces must be true if global.adminPartitions.enabled=true" }}{{ end }} +{{- if and (eq .Values.meshGateway.wanAddress.source "Static") (eq .Values.meshGateway.wanAddress.static "") }}{{ fail "if meshGateway.wanAddress.source=Static then meshGateway.wanAddress.static cannot be empty" }}{{ end }} +{{- if and (eq .Values.meshGateway.wanAddress.source "Service") (eq .Values.meshGateway.service.type "NodePort") (not .Values.meshGateway.service.nodePort) }}{{ fail "if meshGateway.wanAddress.source=Service and meshGateway.service.type=NodePort, meshGateway.service.nodePort must be set" }}{{ end }} +{{ template "consul.validateRequiredCloudSecretsExist" . }} +{{ template "consul.validateCloudSecretKeys" . }} + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "consul.fullname" . }}-mesh-gateway + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: mesh-gateway + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} +spec: + replicas: {{ .Values.meshGateway.replicas }} + selector: + matchLabels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: mesh-gateway + template: + metadata: + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: mesh-gateway + consul.hashicorp.com/connect-inject-managed-by: consul-k8s-endpoints-controller + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} + annotations: + "consul.hashicorp.com/connect-inject": "false" + "consul.hashicorp.com/mesh-inject": "false" + "consul.hashicorp.com/gateway-kind": "mesh-gateway" + "consul.hashicorp.com/gateway-consul-service-name": "{{ .Values.meshGateway.consulServiceName }}" + "consul.hashicorp.com/mesh-gateway-container-port": "{{ .Values.meshGateway.containerPort }}" + "consul.hashicorp.com/gateway-wan-address-source": "{{ .Values.meshGateway.wanAddress.source }}" + "consul.hashicorp.com/gateway-wan-address-static": "{{ .Values.meshGateway.wanAddress.static }}" + {{- if eq .Values.meshGateway.wanAddress.source "Service" }} + {{- if eq .Values.meshGateway.service.type "NodePort" }} + "consul.hashicorp.com/gateway-wan-port": "{{ .Values.meshGateway.service.nodePort }}" + {{- else }} + "consul.hashicorp.com/gateway-wan-port": "{{ .Values.meshGateway.service.port }}" + {{- end }} + {{- else }} + "consul.hashicorp.com/gateway-wan-port": "{{ .Values.meshGateway.wanAddress.port }}" + {{- end }} + {{- if (and .Values.global.secretsBackend.vault.enabled .Values.global.tls.enabled) }} + "vault.hashicorp.com/agent-init-first": "true" + "vault.hashicorp.com/agent-inject": "true" + "vault.hashicorp.com/role": {{ .Values.global.secretsBackend.vault.consulCARole }} + "vault.hashicorp.com/agent-inject-secret-serverca.crt": {{ .Values.global.tls.caCert.secretName }} + "vault.hashicorp.com/agent-inject-template-serverca.crt": {{ template "consul.serverTLSCATemplate" . }} + {{- if and .Values.global.secretsBackend.vault.ca.secretName .Values.global.secretsBackend.vault.ca.secretKey }} + "vault.hashicorp.com/agent-extra-secret": "{{ .Values.global.secretsBackend.vault.ca.secretName }}" + "vault.hashicorp.com/ca-cert": "/vault/custom/{{ .Values.global.secretsBackend.vault.ca.secretKey }}" + {{- end }} + {{- if .Values.global.secretsBackend.vault.agentAnnotations }} + {{ tpl .Values.global.secretsBackend.vault.agentAnnotations . | nindent 8 | trim }} + {{- end }} + {{- if (and (.Values.global.secretsBackend.vault.vaultNamespace) (not (hasKey (default "" .Values.global.secretsBackend.vault.agentAnnotations | fromYaml) "vault.hashicorp.com/namespace")))}} + "vault.hashicorp.com/namespace": "{{ .Values.global.secretsBackend.vault.vaultNamespace }}" + {{- end }} + {{- end }} + {{- if (and .Values.global.metrics.enabled .Values.global.metrics.enableGatewayMetrics) }} + "prometheus.io/scrape": "true" + {{- if not (hasKey (default "" .Values.meshGateway.annotations | fromYaml) "prometheus.io/path")}} + "prometheus.io/path": "/metrics" + {{- end }} + "prometheus.io/port": "20200" + {{- end }} + {{- if .Values.meshGateway.annotations }} + {{- tpl .Values.meshGateway.annotations . | nindent 8 }} + {{- end }} + spec: + {{- if .Values.meshGateway.affinity }} + affinity: + {{ tpl .Values.meshGateway.affinity . | nindent 8 | trim }} + {{- end }} + {{- if .Values.meshGateway.tolerations }} + tolerations: + {{ tpl .Values.meshGateway.tolerations . | nindent 8 | trim }} + {{- end }} + {{- if .Values.meshGateway.topologySpreadConstraints }} + topologySpreadConstraints: + {{ tpl .Values.meshGateway.topologySpreadConstraints . | nindent 8 | trim }} + {{- end }} + terminationGracePeriodSeconds: 10 + serviceAccountName: {{ template "consul.fullname" . }}-mesh-gateway + volumes: + - name: consul-service + emptyDir: + medium: "Memory" + {{- if .Values.global.tls.enabled }} + {{- if not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled) }} + - name: consul-ca-cert + secret: + {{- if .Values.global.tls.caCert.secretName }} + secretName: {{ .Values.global.tls.caCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" . }}-ca-cert + {{- end }} + items: + - key: {{ default "tls.crt" .Values.global.tls.caCert.secretKey }} + path: tls.crt + {{- end }} + {{- end }} + {{- if .Values.meshGateway.hostNetwork }} + hostNetwork: {{ .Values.meshGateway.hostNetwork }} + {{- end }} + {{- if .Values.meshGateway.dnsPolicy }} + dnsPolicy: {{ .Values.meshGateway.dnsPolicy }} + {{- end }} + initContainers: + - name: mesh-gateway-init + image: {{ .Values.global.imageK8S }} + {{ template "consul.imagePullPolicy" . }} + env: + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + {{- include "consul.consulK8sConsulServerEnvVars" . | nindent 8 }} + {{- if .Values.global.acls.manageSystemACLs }} + - name: CONSUL_LOGIN_AUTH_METHOD + {{- if and .Values.global.federation.enabled .Values.global.federation.primaryDatacenter }} + value: {{ template "consul.fullname" . }}-k8s-component-auth-method-{{ .Values.global.datacenter }} + {{- else }} + value: {{ template "consul.fullname" . }}-k8s-component-auth-method + {{- end }} + - name: CONSUL_LOGIN_DATACENTER + {{- if and .Values.global.federation.enabled .Values.global.federation.primaryDatacenter }} + value: {{ .Values.global.federation.primaryDatacenter }} + {{- else }} + value: {{ .Values.global.datacenter }} + {{- end }} + - name: CONSUL_LOGIN_META + value: "component=mesh-gateway,pod=$(NAMESPACE)/$(POD_NAME)" + {{- end }} + - name: CONSUL_NODE_NAME + value: $(NODE_NAME)-virtual + command: + - "/bin/sh" + - "-ec" + - | + exec consul-k8s-control-plane connect-init -pod-name=${POD_NAME} -pod-namespace=${NAMESPACE} \ + -gateway-kind="mesh-gateway" \ + -proxy-id-file=/consul/service/proxy-id \ + -service-name={{ .Values.meshGateway.consulServiceName }} \ + -log-level={{ default .Values.global.logLevel .Values.meshGateway.logLevel }} \ + -log-json={{ .Values.global.logJSON }} + volumeMounts: + - name: consul-service + mountPath: /consul/service + {{- if .Values.global.tls.enabled }} + {{- if not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled) }} + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- end }} + {{- if .Values.meshGateway.initServiceInitContainer.resources }} + resources: {{ toYaml .Values.meshGateway.initServiceInitContainer.resources | nindent 10 }} + {{- end }} + containers: + - name: mesh-gateway + image: {{ .Values.global.imageConsulDataplane | quote }} + {{ template "consul.imagePullPolicy" . }} + securityContext: + capabilities: + {{ if not .Values.meshGateway.hostNetwork}} + drop: + - ALL + {{- end }} + add: + - NET_BIND_SERVICE + {{- if .Values.meshGateway.resources }} + resources: + {{- if eq (typeOf .Values.meshGateway.resources) "string" }} + {{ tpl .Values.meshGateway.resources . | nindent 12 | trim }} + {{- else }} + {{- toYaml .Values.meshGateway.resources | nindent 12 }} + {{- end }} + {{- end }} + volumeMounts: + - mountPath: /consul/service + name: consul-service + readOnly: true + {{- if .Values.global.tls.enabled }} + {{- if not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled) }} + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- end }} + env: + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: DP_CREDENTIAL_LOGIN_META1 + value: pod=$(NAMESPACE)/$(POD_NAME) + - name: DP_CREDENTIAL_LOGIN_META2 + value: component=mesh-gateway + - name: DP_SERVICE_NODE_NAME + value: $(NODE_NAME)-virtual + command: + - consul-dataplane + args: + {{- if .Values.externalServers.enabled }} + - -addresses={{ .Values.externalServers.hosts | first }} + {{- else }} + - -addresses={{ template "consul.fullname" . }}-server.{{ .Release.Namespace }}.svc + {{- end }} + {{- if .Values.externalServers.enabled }} + - -grpc-port={{ .Values.externalServers.grpcPort }} + {{- else }} + - -grpc-port=8502 + {{- end }} + - -proxy-service-id-path=/consul/service/proxy-id + {{- if .Values.global.tls.enabled }} + {{- if (not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots)) }} + {{- if .Values.global.secretsBackend.vault.enabled }} + - -ca-certs=/vault/secrets/serverca.crt + {{- else }} + - -ca-certs=/consul/tls/ca/tls.crt + {{- end }} + {{- end }} + {{- if and .Values.externalServers.enabled .Values.externalServers.tlsServerName }} + - -tls-server-name={{.Values.externalServers.tlsServerName }} + {{- else if .Values.global.cloud.enabled }} + - -tls-server-name=server.{{ .Values.global.datacenter}}.{{ .Values.global.domain}} + {{- end }} + {{- else }} + - -tls-disabled + {{- end }} + {{- if .Values.global.acls.manageSystemACLs }} + - -credential-type=login + - -login-bearer-token-path=/var/run/secrets/kubernetes.io/serviceaccount/token + {{- if and .Values.global.federation.enabled .Values.global.federation.primaryDatacenter }} + - -login-auth-method={{ template "consul.fullname" . }}-k8s-component-auth-method-{{ .Values.global.datacenter }} + - -login-datacenter={{ .Values.global.federation.primaryDatacenter }} + {{- else }} + - -login-auth-method={{ template "consul.fullname" . }}-k8s-component-auth-method + {{- end }} + {{- if .Values.global.adminPartitions.enabled }} + - -login-partition={{ .Values.global.adminPartitions.name }} + {{- end }} + {{- end }} + {{- if .Values.global.adminPartitions.enabled }} + - -service-partition={{ .Values.global.adminPartitions.name }} + {{- end }} + - -log-level={{ default .Values.global.logLevel .Values.meshGateway.logLevel }} + - -log-json={{ .Values.global.logJSON }} + {{- if (and .Values.global.metrics.enabled .Values.global.metrics.enableGatewayMetrics) }} + - -telemetry-prom-scrape-path=/metrics + {{- end }} + {{- if and .Values.externalServers.enabled .Values.externalServers.skipServerWatch }} + - -server-watch-disabled=true + {{- end }} + livenessProbe: + tcpSocket: + port: {{ .Values.meshGateway.containerPort }} + failureThreshold: 3 + initialDelaySeconds: 30 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + readinessProbe: + tcpSocket: + port: {{ .Values.meshGateway.containerPort }} + failureThreshold: 3 + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + ports: + - name: gateway + containerPort: {{ .Values.meshGateway.containerPort }} + {{- if .Values.meshGateway.hostPort }} + hostPort: {{ .Values.meshGateway.hostPort }} + {{- end }} + {{- if .Values.meshGateway.priorityClassName }} + priorityClassName: {{ .Values.meshGateway.priorityClassName | quote }} + {{- end }} + {{- if .Values.meshGateway.nodeSelector }} + nodeSelector: + {{ tpl .Values.meshGateway.nodeSelector . | indent 8 | trim }} + {{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/mesh-gateway-podsecuritypolicy.yaml b/charts/hashicorp/consul/1.5.3/templates/mesh-gateway-podsecuritypolicy.yaml new file mode 100644 index 0000000000..56e4b7924c --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/mesh-gateway-podsecuritypolicy.yaml @@ -0,0 +1,56 @@ +{{- if and .Values.global.enablePodSecurityPolicies .Values.meshGateway.enabled }} +{{- if not (mustHas "resource-apis" .Values.global.experiments) }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-mesh-gateway + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: mesh-gateway +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + defaultAddCapabilities: + - NET_BIND_SERVICE + # Allow core volume types. + volumes: + - 'configMap' + - 'emptyDir' + - 'projected' + - 'secret' + - 'downwardAPI' + {{- if .Values.meshGateway.hostNetwork }} + hostNetwork: {{ .Values.meshGateway.hostNetwork }} + {{- else }} + hostNetwork: false + {{- end }} + hostPorts: + {{- if .Values.meshGateway.hostPort }} + - min: {{ .Values.meshGateway.hostPort }} + max: {{ .Values.meshGateway.hostPort }} + {{- else if .Values.meshGateway.hostNetwork }} + - min: {{ .Values.meshGateway.containerPort }} + max: {{ .Values.meshGateway.containerPort }} + {{- end }} + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/mesh-gateway-service.yaml b/charts/hashicorp/consul/1.5.3/templates/mesh-gateway-service.yaml new file mode 100644 index 0000000000..80f82ac897 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/mesh-gateway-service.yaml @@ -0,0 +1,35 @@ +{{- if and .Values.meshGateway.enabled }} +{{- if not (mustHas "resource-apis" .Values.global.experiments) }} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "consul.fullname" . }}-mesh-gateway + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: mesh-gateway + {{- if .Values.meshGateway.service.annotations }} + annotations: + {{ tpl .Values.meshGateway.service.annotations . | nindent 4 | trim }} + {{- end }} +spec: + selector: + app: {{ template "consul.name" . }} + release: "{{ .Release.Name }}" + component: mesh-gateway + ports: + - name: gateway + port: {{ .Values.meshGateway.service.port }} + targetPort: {{ .Values.meshGateway.containerPort }} + {{- if .Values.meshGateway.service.nodePort }} + nodePort: {{ .Values.meshGateway.service.nodePort }} + {{- end}} + type: {{ .Values.meshGateway.service.type }} + {{- if .Values.meshGateway.service.additionalSpec }} + {{ tpl .Values.meshGateway.service.additionalSpec . | nindent 2 | trim }} + {{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/mesh-gateway-serviceaccount.yaml b/charts/hashicorp/consul/1.5.3/templates/mesh-gateway-serviceaccount.yaml new file mode 100644 index 0000000000..b1a0661eaa --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/mesh-gateway-serviceaccount.yaml @@ -0,0 +1,25 @@ +{{- if .Values.meshGateway.enabled }} +{{- if not (mustHas "resource-apis" .Values.global.experiments) }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-mesh-gateway + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: mesh-gateway + {{- if .Values.meshGateway.serviceAccount.annotations }} + annotations: + {{ tpl .Values.meshGateway.serviceAccount.annotations . | nindent 4 | trim }} + {{- end }} +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/partition-init-job.yaml b/charts/hashicorp/consul/1.5.3/templates/partition-init-job.yaml new file mode 100644 index 0000000000..0ce8a921b2 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/partition-init-job.yaml @@ -0,0 +1,132 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (and .Values.global.adminPartitions.enabled (not $serverEnabled) (ne .Values.global.adminPartitions.name "default")) }} +{{- template "consul.reservedNamesFailer" (list .Values.global.adminPartitions.name "global.adminPartitions.name") }} +{{- if and (not .Values.externalServers.enabled) (ne .Values.global.adminPartitions.name "default") }}{{ fail "externalServers.enabled needs to be true and configured to create a non-default partition." }}{{ end -}} +{{- if and .Values.global.secretsBackend.vault.enabled .Values.global.acls.manageSystemACLs (not .Values.global.secretsBackend.vault.adminPartitionsRole) }}{{ fail "global.secretsBackend.vault.adminPartitionsRole is required when global.secretsBackend.vault.enabled and global.acls.manageSystemACLs are true." }}{{ end -}} +{{- if and .Values.externalServers.enabled (not .Values.externalServers.hosts) }}{{ fail "externalServers.hosts must be set if externalServers.enabled is true" }}{{ end -}} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "consul.fullname" . }}-partition-init + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: partition-init + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} + annotations: + "helm.sh/hook": pre-install + "helm.sh/hook-weight": "2" + "helm.sh/hook-delete-policy": hook-succeeded,before-hook-creation +spec: + template: + metadata: + name: {{ template "consul.fullname" . }}-partition-init + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: partition-init + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} + annotations: + "consul.hashicorp.com/connect-inject": "false" + "consul.hashicorp.com/mesh-inject": "false" + {{- if (and .Values.global.secretsBackend.vault.enabled (or .Values.global.tls.enabled .Values.global.acls.manageSystemACLs)) }} + "vault.hashicorp.com/agent-pre-populate-only": "true" + "vault.hashicorp.com/agent-inject": "true" + {{- if .Values.global.acls.manageSystemACLs }} + "vault.hashicorp.com/role": {{ .Values.global.secretsBackend.vault.adminPartitionsRole }} + {{- if .Values.global.acls.bootstrapToken.secretName }} + {{- with .Values.global.acls.bootstrapToken }} + "vault.hashicorp.com/agent-inject-secret-bootstrap-token": "{{ .secretName }}" + "vault.hashicorp.com/agent-inject-template-bootstrap-token": {{ template "consul.vaultSecretTemplate" . }} + {{- end }} + {{- end }} + {{- else }} + "vault.hashicorp.com/role": {{ .Values.global.secretsBackend.vault.consulCARole }} + {{- end }} + "vault.hashicorp.com/agent-inject-secret-serverca.crt": {{ .Values.global.tls.caCert.secretName }} + "vault.hashicorp.com/agent-inject-template-serverca.crt": {{ template "consul.serverTLSCATemplate" . }} + {{- if and .Values.global.secretsBackend.vault.ca.secretName .Values.global.secretsBackend.vault.ca.secretKey }} + "vault.hashicorp.com/agent-extra-secret": "{{ .Values.global.secretsBackend.vault.ca.secretName }}" + "vault.hashicorp.com/ca-cert": "/vault/custom/{{ .Values.global.secretsBackend.vault.ca.secretKey }}" + {{- end }} + {{- if .Values.global.secretsBackend.vault.agentAnnotations }} + {{ tpl .Values.global.secretsBackend.vault.agentAnnotations . | nindent 8 | trim }} + {{- end }} + {{- if (and (.Values.global.secretsBackend.vault.vaultNamespace) (not (hasKey (default "" .Values.global.secretsBackend.vault.agentAnnotations | fromYaml) "vault.hashicorp.com/namespace")))}} + "vault.hashicorp.com/namespace": "{{ .Values.global.secretsBackend.vault.vaultNamespace }}" + {{- end }} + {{- end }} + spec: + restartPolicy: Never + serviceAccountName: {{ template "consul.fullname" . }}-partition-init + {{- if .Values.global.tls.enabled }} + {{- if not (or .Values.externalServers.useSystemRoots .Values.global.secretsBackend.vault.enabled) }} + volumes: + - name: consul-ca-cert + secret: + {{- if .Values.global.tls.caCert.secretName }} + secretName: {{ .Values.global.tls.caCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" . }}-ca-cert + {{- end }} + items: + - key: {{ default "tls.crt" .Values.global.tls.caCert.secretKey }} + path: tls.crt + {{- end }} + {{- end }} + containers: + - name: partition-init-job + image: {{ .Values.global.imageK8S }} + {{ template "consul.imagePullPolicy" . }} + {{- include "consul.restrictedSecurityContext" . | nindent 10 }} + env: + {{- include "consul.consulK8sConsulServerEnvVars" . | nindent 10 }} + {{- if (and .Values.global.acls.bootstrapToken.secretName .Values.global.acls.bootstrapToken.secretKey) }} + {{- if .Values.global.secretsBackend.vault.enabled }} + - name: CONSUL_ACL_TOKEN_FILE + value: /vault/secrets/bootstrap-token + {{- else }} + - name: CONSUL_ACL_TOKEN + valueFrom: + secretKeyRef: + name: {{ .Values.global.acls.bootstrapToken.secretName }} + key: {{ .Values.global.acls.bootstrapToken.secretKey }} + {{- end }} + {{- end }} + {{- if .Values.global.tls.enabled }} + {{- if not (or .Values.externalServers.useSystemRoots .Values.global.secretsBackend.vault.enabled) }} + volumeMounts: + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- end }} + command: + - "/bin/sh" + - "-ec" + - | + exec consul-k8s-control-plane partition-init \ + -log-level={{ .Values.global.logLevel }} \ + -log-json={{ .Values.global.logJSON }} \ + {{- if .Values.global.cloud.enabled }} + -tls-server-name=server.{{ .Values.global.datacenter}}.{{ .Values.global.domain}} \ + {{- end }} + {{- if and (mustHas "resource-apis" .Values.global.experiments) (mustHas "v2tenancy" .Values.global.experiments) }} + -enable-v2tenancy=true + {{- end }} + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/partition-init-podsecuritypolicy.yaml b/charts/hashicorp/consul/1.5.3/templates/partition-init-podsecuritypolicy.yaml new file mode 100644 index 0000000000..2bc6782394 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/partition-init-podsecuritypolicy.yaml @@ -0,0 +1,40 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (and .Values.global.adminPartitions.enabled .Values.global.enablePodSecurityPolicies (not $serverEnabled)) }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-partition-init + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: partition-init + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation +spec: + privileged: false + # Allow core volume types. + volumes: + - 'secret' + - 'emptyDir' + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/partition-init-role.yaml b/charts/hashicorp/consul/1.5.3/templates/partition-init-role.yaml new file mode 100644 index 0000000000..340c59ed95 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/partition-init-role.yaml @@ -0,0 +1,35 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (and .Values.global.adminPartitions.enabled (not $serverEnabled)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" . }}-partition-init + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: partition-init + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation +rules: +{{- if .Values.connectInject.enabled }} + - apiGroups: [""] + resources: + - serviceaccounts + resourceNames: + - {{ template "consul.fullname" . }}-connect-injector + verbs: + - get +{{- end }} +{{- if .Values.global.enablePodSecurityPolicies }} + - apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + resourceNames: + - {{ template "consul.fullname" . }}-partition-init + verbs: + - use +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/partition-init-rolebinding.yaml b/charts/hashicorp/consul/1.5.3/templates/partition-init-rolebinding.yaml new file mode 100644 index 0000000000..432d6df6ec --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/partition-init-rolebinding.yaml @@ -0,0 +1,24 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (and .Values.global.adminPartitions.enabled (not $serverEnabled)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" . }}-partition-init + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: partition-init + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" . }}-partition-init +subjects: + - kind: ServiceAccount + name: {{ template "consul.fullname" . }}-partition-init +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/partition-init-serviceaccount.yaml b/charts/hashicorp/consul/1.5.3/templates/partition-init-serviceaccount.yaml new file mode 100644 index 0000000000..65fcf43b08 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/partition-init-serviceaccount.yaml @@ -0,0 +1,23 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (and .Values.global.adminPartitions.enabled (not $serverEnabled)) }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-partition-init + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: partition-init + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/partition-name-configmap.yaml b/charts/hashicorp/consul/1.5.3/templates/partition-name-configmap.yaml new file mode 100644 index 0000000000..ee330b0f46 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/partition-name-configmap.yaml @@ -0,0 +1,19 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (and .Values.global.adminPartitions.enabled (not $serverEnabled)) }} +# Immutable ConfigMap which saves the partition name. Attempting to update this configmap +# with a new Admin Partition name will cause the helm upgrade to fail +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "consul.fullname" . }}-partition + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: partition-init +immutable: true +data: + partitionName: {{ .Values.global.adminPartitions.name }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/prometheus.yaml b/charts/hashicorp/consul/1.5.3/templates/prometheus.yaml new file mode 100644 index 0000000000..a708708daf --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/prometheus.yaml @@ -0,0 +1,488 @@ +{{- if .Values.prometheus.enabled }} +# This file is auto-generated, see addons/gen.sh +--- +# Source: prometheus/templates/server/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + component: "server" + app: prometheus + release: prometheus + chart: prometheus-13.2.1 + heritage: Helm + name: prometheus-server + namespace: {{ .Release.Namespace }} + annotations: + {} +--- +# Source: prometheus/templates/server/cm.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + component: "server" + app: prometheus + release: prometheus + chart: prometheus-13.2.1 + heritage: Helm + name: prometheus-server + namespace: {{ .Release.Namespace }} +data: + alerting_rules.yml: | + {} + alerts: | + {} + prometheus.yml: | + global: + evaluation_interval: 1m + scrape_interval: 15s + scrape_timeout: 10s + rule_files: + - /etc/config/recording_rules.yml + - /etc/config/alerting_rules.yml + - /etc/config/rules + - /etc/config/alerts + scrape_configs: + - job_name: prometheus + static_configs: + - targets: + - localhost:9090 + - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token + job_name: kubernetes-apiservers + kubernetes_sd_configs: + - role: endpoints + relabel_configs: + - action: keep + regex: default;kubernetes;https + source_labels: + - __meta_kubernetes_namespace + - __meta_kubernetes_service_name + - __meta_kubernetes_endpoint_port_name + scheme: https + tls_config: + ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + insecure_skip_verify: true + - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token + job_name: kubernetes-nodes + kubernetes_sd_configs: + - role: node + relabel_configs: + - action: labelmap + regex: __meta_kubernetes_node_label_(.+) + - replacement: kubernetes.default.svc:443 + target_label: __address__ + - regex: (.+) + replacement: /api/v1/nodes/$1/proxy/metrics + source_labels: + - __meta_kubernetes_node_name + target_label: __metrics_path__ + scheme: https + tls_config: + ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + insecure_skip_verify: true + - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token + job_name: kubernetes-nodes-cadvisor + kubernetes_sd_configs: + - role: node + relabel_configs: + - action: labelmap + regex: __meta_kubernetes_node_label_(.+) + - replacement: kubernetes.default.svc:443 + target_label: __address__ + - regex: (.+) + replacement: /api/v1/nodes/$1/proxy/metrics/cadvisor + source_labels: + - __meta_kubernetes_node_name + target_label: __metrics_path__ + scheme: https + tls_config: + ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + insecure_skip_verify: true + - job_name: kubernetes-service-endpoints + kubernetes_sd_configs: + - role: endpoints + relabel_configs: + - action: keep + regex: true + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_scrape + - action: replace + regex: (https?) + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_scheme + target_label: __scheme__ + - action: replace + regex: (.+) + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_path + target_label: __metrics_path__ + - action: replace + regex: ([^:]+)(?::\d+)?;(\d+) + replacement: $1:$2 + source_labels: + - __address__ + - __meta_kubernetes_service_annotation_prometheus_io_port + target_label: __address__ + - action: labelmap + regex: __meta_kubernetes_service_label_(.+) + - action: replace + source_labels: + - __meta_kubernetes_namespace + target_label: kubernetes_namespace + - action: replace + source_labels: + - __meta_kubernetes_service_name + target_label: kubernetes_name + - action: replace + source_labels: + - __meta_kubernetes_pod_node_name + target_label: kubernetes_node + - job_name: kubernetes-service-endpoints-slow + kubernetes_sd_configs: + - role: endpoints + relabel_configs: + - action: keep + regex: true + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_scrape_slow + - action: replace + regex: (https?) + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_scheme + target_label: __scheme__ + - action: replace + regex: (.+) + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_path + target_label: __metrics_path__ + - action: replace + regex: ([^:]+)(?::\d+)?;(\d+) + replacement: $1:$2 + source_labels: + - __address__ + - __meta_kubernetes_service_annotation_prometheus_io_port + target_label: __address__ + - action: labelmap + regex: __meta_kubernetes_service_label_(.+) + - action: replace + source_labels: + - __meta_kubernetes_namespace + target_label: kubernetes_namespace + - action: replace + source_labels: + - __meta_kubernetes_service_name + target_label: kubernetes_name + - action: replace + source_labels: + - __meta_kubernetes_pod_node_name + target_label: kubernetes_node + scrape_interval: 5m + scrape_timeout: 30s + - honor_labels: true + job_name: prometheus-pushgateway + kubernetes_sd_configs: + - role: service + relabel_configs: + - action: keep + regex: pushgateway + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_probe + - job_name: kubernetes-services + kubernetes_sd_configs: + - role: service + metrics_path: /probe + params: + module: + - http_2xx + relabel_configs: + - action: keep + regex: true + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_probe + - source_labels: + - __address__ + target_label: __param_target + - replacement: blackbox + target_label: __address__ + - source_labels: + - __param_target + target_label: instance + - action: labelmap + regex: __meta_kubernetes_service_label_(.+) + - source_labels: + - __meta_kubernetes_namespace + target_label: kubernetes_namespace + - source_labels: + - __meta_kubernetes_service_name + target_label: kubernetes_name + - job_name: kubernetes-pods + kubernetes_sd_configs: + - role: pod + relabel_configs: + - action: keep + regex: true + source_labels: + - __meta_kubernetes_pod_annotation_prometheus_io_scrape + - action: replace + regex: (https?) + source_labels: + - __meta_kubernetes_pod_annotation_prometheus_io_scheme + target_label: __scheme__ + - action: replace + regex: (.+) + source_labels: + - __meta_kubernetes_pod_annotation_prometheus_io_path + target_label: __metrics_path__ + - action: replace + regex: ([^:]+)(?::\d+)?;(\d+) + replacement: $1:$2 + source_labels: + - __address__ + - __meta_kubernetes_pod_annotation_prometheus_io_port + target_label: __address__ + - action: labelmap + regex: __meta_kubernetes_pod_label_(.+) + - action: replace + source_labels: + - __meta_kubernetes_namespace + target_label: kubernetes_namespace + - action: replace + source_labels: + - __meta_kubernetes_pod_name + target_label: kubernetes_pod_name + - action: drop + regex: Pending|Succeeded|Failed + source_labels: + - __meta_kubernetes_pod_phase + - job_name: kubernetes-pods-slow + kubernetes_sd_configs: + - role: pod + relabel_configs: + - action: keep + regex: true + source_labels: + - __meta_kubernetes_pod_annotation_prometheus_io_scrape_slow + - action: replace + regex: (https?) + source_labels: + - __meta_kubernetes_pod_annotation_prometheus_io_scheme + target_label: __scheme__ + - action: replace + regex: (.+) + source_labels: + - __meta_kubernetes_pod_annotation_prometheus_io_path + target_label: __metrics_path__ + - action: replace + regex: ([^:]+)(?::\d+)?;(\d+) + replacement: $1:$2 + source_labels: + - __address__ + - __meta_kubernetes_pod_annotation_prometheus_io_port + target_label: __address__ + - action: labelmap + regex: __meta_kubernetes_pod_label_(.+) + - action: replace + source_labels: + - __meta_kubernetes_namespace + target_label: kubernetes_namespace + - action: replace + source_labels: + - __meta_kubernetes_pod_name + target_label: kubernetes_pod_name + - action: drop + regex: Pending|Succeeded|Failed + source_labels: + - __meta_kubernetes_pod_phase + scrape_interval: 5m + scrape_timeout: 30s + recording_rules.yml: | + {} + rules: | + {} +--- +# Source: prometheus/templates/server/clusterrole.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + component: "server" + app: prometheus + release: prometheus + chart: prometheus-13.2.1 + heritage: Helm + name: prometheus-server +rules: + - apiGroups: + - "" + resources: + - nodes + - nodes/proxy + - nodes/metrics + - services + - endpoints + - pods + - ingresses + - configmaps + verbs: + - get + - list + - watch + - apiGroups: + - "extensions" + - "networking.k8s.io" + resources: + - ingresses/status + - ingresses + verbs: + - get + - list + - watch + - nonResourceURLs: + - "/metrics" + verbs: + - get +--- +# Source: prometheus/templates/server/clusterrolebinding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + component: "server" + app: prometheus + release: prometheus + chart: prometheus-13.2.1 + heritage: Helm + name: prometheus-server +subjects: + - kind: ServiceAccount + name: prometheus-server + namespace: {{ .Release.Namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: prometheus-server +--- +# Source: prometheus/templates/server/service.yaml +apiVersion: v1 +kind: Service +metadata: + labels: + component: "server" + app: prometheus + release: prometheus + chart: prometheus-13.2.1 + heritage: Helm + name: prometheus-server + namespace: {{ .Release.Namespace }} +spec: + ports: + - name: http + port: 80 + protocol: TCP + targetPort: 9090 + selector: + component: "server" + app: prometheus + release: prometheus + sessionAffinity: None + type: "ClusterIP" +--- +# Source: prometheus/templates/server/deploy.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + component: "server" + app: prometheus + release: prometheus + chart: prometheus-13.2.1 + heritage: Helm + name: prometheus-server + namespace: {{ .Release.Namespace }} +spec: + selector: + matchLabels: + component: "server" + app: prometheus + release: prometheus + replicas: 1 + template: + metadata: + annotations: + consul.hashicorp.com/connect-inject: "false" + consul.hashicorp.com/mesh-inject: "false" + labels: + component: "server" + app: prometheus + release: prometheus + chart: prometheus-13.2.1 + heritage: Helm + spec: + serviceAccountName: prometheus-server + containers: + - name: prometheus-server-configmap-reload + image: "jimmidyson/configmap-reload:v0.4.0" + imagePullPolicy: "IfNotPresent" + args: + - --volume-dir=/etc/config + - --webhook-url=http://127.0.0.1:9090/-/reload + resources: + {} + volumeMounts: + - name: config-volume + mountPath: /etc/config + readOnly: true + + - name: prometheus-server + image: "quay.io/prometheus/prometheus:v2.24.0" + imagePullPolicy: "IfNotPresent" + args: + - --storage.tsdb.retention.time=15d + - --config.file=/etc/config/prometheus.yml + - --storage.tsdb.path=/data + - --web.console.libraries=/etc/prometheus/console_libraries + - --web.console.templates=/etc/prometheus/consoles + - --web.enable-lifecycle + ports: + - containerPort: 9090 + readinessProbe: + httpGet: + path: /-/ready + port: 9090 + initialDelaySeconds: 0 + periodSeconds: 5 + timeoutSeconds: 4 + failureThreshold: 3 + successThreshold: 1 + livenessProbe: + httpGet: + path: /-/healthy + port: 9090 + initialDelaySeconds: 30 + periodSeconds: 15 + timeoutSeconds: 10 + failureThreshold: 3 + successThreshold: 1 + resources: + {} + volumeMounts: + - name: config-volume + mountPath: /etc/config + - name: storage-volume + mountPath: /data + subPath: "" + securityContext: + fsGroup: 65534 + runAsGroup: 65534 + runAsNonRoot: true + runAsUser: 65534 + terminationGracePeriodSeconds: 300 + volumes: + - name: config-volume + configMap: + name: prometheus-server + - name: storage-volume + emptyDir: + {} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/server-acl-init-cleanup-job.yaml b/charts/hashicorp/consul/1.5.3/templates/server-acl-init-cleanup-job.yaml new file mode 100644 index 0000000000..3d7d6c8120 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/server-acl-init-cleanup-job.yaml @@ -0,0 +1,90 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (or $serverEnabled .Values.externalServers.enabled) }} +{{- if .Values.global.acls.manageSystemACLs }} +{{- /* See reason for this in server-acl-init-job.yaml */ -}} +{{- if eq (int .Values.server.updatePartition) 0 }} +# This job deletes the server-acl-init job once it completes successfully. +# It runs as a helm hook because it only needs to run when the server-acl-init +# Job gets recreated which only happens during an install or upgrade. +# We also utilize the helm hook-delete-policy to delete this job itself. +# We want to delete the server-acl-init job because once it runs successfully +# it's not needed and also because if it stays around then when users run +# helm upgrade with values that change the spec of the job, Kubernetes errors +# because the job spec is immutable. If the job is deleted, then a new job +# is created and there's no error. +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "consul.fullname" . }}-server-acl-init-cleanup + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server-acl-init-cleanup + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-weight": "0" + # If the hook fails then all that happens is we didn't delete the job. + # There's no reason for *this* job to stay around in that case so delete + # regardless of success. + "helm.sh/hook-delete-policy": hook-succeeded,hook-failed +spec: + template: + metadata: + name: {{ template "consul.fullname" . }}-server-acl-init-cleanup + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: server-acl-init-cleanup + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} + annotations: + "consul.hashicorp.com/connect-inject": "false" + "consul.hashicorp.com/mesh-inject": "false" + {{- if .Values.global.acls.annotations }} + {{- tpl .Values.global.acls.annotations . | nindent 8 }} + {{- end }} + spec: + restartPolicy: Never + serviceAccountName: {{ template "consul.fullname" . }}-server-acl-init-cleanup + {{- if .Values.server.containerSecurityContext.aclInit }} + securityContext: + {{- toYaml .Values.server.containerSecurityContext.aclInit | nindent 8 }} + {{- end }} + containers: + - name: server-acl-init-cleanup + image: {{ .Values.global.imageK8S }} + {{ template "consul.imagePullPolicy" . }} + {{- if not .Values.server.containerSecurityContext.aclInit }} + {{- include "consul.restrictedSecurityContext" . | nindent 10 }} + {{- end }} + command: + - consul-k8s-control-plane + args: + - delete-completed-job + - -log-level={{ default .Values.global.logLevel .Values.global.acls.logLevel }} + - -log-json={{ .Values.global.logJSON }} + - -k8s-namespace={{ .Release.Namespace }} + - {{ template "consul.fullname" . }}-server-acl-init + {{- if .Values.global.acls.resources }} + resources: + {{- toYaml .Values.global.acls.resources | nindent 12 }} + {{- end }} + {{- if .Values.global.acls.tolerations }} + tolerations: + {{ tpl .Values.global.acls.tolerations . | indent 8 | trim }} + {{- end }} + {{- if .Values.global.acls.nodeSelector }} + nodeSelector: + {{ tpl .Values.global.acls.nodeSelector . | indent 8 | trim }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/server-acl-init-cleanup-podsecuritypolicy.yaml b/charts/hashicorp/consul/1.5.3/templates/server-acl-init-cleanup-podsecuritypolicy.yaml new file mode 100644 index 0000000000..dd5dad24df --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/server-acl-init-cleanup-podsecuritypolicy.yaml @@ -0,0 +1,40 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (or $serverEnabled .Values.externalServers.enabled) }} +{{- if .Values.global.acls.manageSystemACLs }} +{{- if .Values.global.enablePodSecurityPolicies }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-server-acl-init-cleanup + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server-acl-init-cleanup +spec: + privileged: false + # Allow core volume types. + volumes: + - 'secret' + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false + {{- end }} + {{- end }} + {{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/server-acl-init-cleanup-role.yaml b/charts/hashicorp/consul/1.5.3/templates/server-acl-init-cleanup-role.yaml new file mode 100644 index 0000000000..0a2f296a60 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/server-acl-init-cleanup-role.yaml @@ -0,0 +1,28 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (or $serverEnabled .Values.externalServers.enabled) }} +{{- if .Values.global.acls.manageSystemACLs }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" . }}-server-acl-init-cleanup + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server-acl-init-cleanup +rules: + - apiGroups: ["batch"] + resources: ["jobs"] + verbs: ["get", "delete"] +{{- if .Values.global.enablePodSecurityPolicies }} + - apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + resourceNames: + - {{ template "consul.fullname" . }}-server-acl-init-cleanup + verbs: + - use +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/server-acl-init-cleanup-rolebinding.yaml b/charts/hashicorp/consul/1.5.3/templates/server-acl-init-cleanup-rolebinding.yaml new file mode 100644 index 0000000000..268eaa5677 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/server-acl-init-cleanup-rolebinding.yaml @@ -0,0 +1,23 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (or $serverEnabled .Values.externalServers.enabled) }} +{{- if .Values.global.acls.manageSystemACLs }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" . }}-server-acl-init-cleanup + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server-acl-init-cleanup +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" . }}-server-acl-init-cleanup +subjects: + - kind: ServiceAccount + name: {{ template "consul.fullname" . }}-server-acl-init-cleanup +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/server-acl-init-cleanup-serviceaccount.yaml b/charts/hashicorp/consul/1.5.3/templates/server-acl-init-cleanup-serviceaccount.yaml new file mode 100644 index 0000000000..604e6d784c --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/server-acl-init-cleanup-serviceaccount.yaml @@ -0,0 +1,22 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (or $serverEnabled .Values.externalServers.enabled) }} +{{- if .Values.global.acls.manageSystemACLs }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-server-acl-init-cleanup + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server-acl-init-cleanup +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/server-acl-init-job.yaml b/charts/hashicorp/consul/1.5.3/templates/server-acl-init-job.yaml new file mode 100644 index 0000000000..0156c60f74 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/server-acl-init-job.yaml @@ -0,0 +1,350 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (and $serverEnabled .Values.externalServers.enabled) }}{{ fail "only one of server.enabled or externalServers.enabled can be set" }}{{ end -}} +{{- if (or $serverEnabled .Values.externalServers.enabled) }} +{{- if and .Values.global.acls.createReplicationToken (not .Values.global.acls.manageSystemACLs) }}{{ fail "if global.acls.createReplicationToken is true, global.acls.manageSystemACLs must be true" }}{{ end -}} +{{- if .Values.global.bootstrapACLs }}{{ fail "global.bootstrapACLs was removed, use global.acls.manageSystemACLs instead" }}{{ end -}} +{{- if .Values.global.acls.manageSystemACLs }} +{{- if or (and .Values.global.acls.bootstrapToken.secretName (not .Values.global.acls.bootstrapToken.secretKey)) (and .Values.global.acls.bootstrapToken.secretKey (not .Values.global.acls.bootstrapToken.secretName))}}{{ fail "both global.acls.bootstrapToken.secretKey and global.acls.bootstrapToken.secretName must be set if one of them is provided" }}{{ end -}} +{{- if or (and .Values.global.acls.replicationToken.secretName (not .Values.global.acls.replicationToken.secretKey)) (and .Values.global.acls.replicationToken.secretKey (not .Values.global.acls.replicationToken.secretName))}}{{ fail "both global.acls.replicationToken.secretKey and global.acls.replicationToken.secretName must be set if one of them is provided" }}{{ end -}} +{{- if (and .Values.global.secretsBackend.vault.enabled (and (not .Values.global.acls.bootstrapToken.secretName) (not .Values.global.acls.replicationToken.secretName ))) }}{{fail "global.acls.bootstrapToken or global.acls.replicationToken must be provided when global.secretsBackend.vault.enabled and global.acls.manageSystemACLs are true" }}{{ end -}} +{{ template "consul.validateRequiredCloudSecretsExist" . }} +{{ template "consul.validateCloudSecretKeys" . }} +{{- if (and .Values.global.secretsBackend.vault.enabled (not .Values.global.secretsBackend.vault.manageSystemACLsRole)) }}{{fail "global.secretsBackend.vault.manageSystemACLsRole is required when global.secretsBackend.vault.enabled and global.acls.manageSystemACLs are true" }}{{ end -}} + {{- /* We don't render this job when server.updatePartition > 0 because that + means a server rollout is in progress and this job won't complete unless + the rollout is finished (which won't happen until the partition is 0). + If we ran it in this case, then the job would not complete which would cause + the server-acl-init-cleanup hook to run indefinitely which would cause the + helm upgrade command to hang. +*/ -}} +{{- if eq (int .Values.server.updatePartition) 0 }} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "consul.fullname" . }}-server-acl-init + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server-acl-init + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} +spec: + template: + metadata: + name: {{ template "consul.fullname" . }}-server-acl-init + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: server-acl-init + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} + annotations: + "consul.hashicorp.com/connect-inject": "false" + "consul.hashicorp.com/mesh-inject": "false" + {{- if .Values.global.acls.annotations }} + {{- tpl .Values.global.acls.annotations . | nindent 8 }} + {{- end }} + {{- if .Values.global.argocd.enabled }} + "argocd.argoproj.io/hook": "Sync" + "argocd.argoproj.io/hook-delete-policy": "HookSucceeded" + {{- end }} + {{- if .Values.global.secretsBackend.vault.enabled }} + + {{- /* Run the Vault agent as both an init container and sidecar. + The Vault agent sidecar is needed when server-acl-init bootstraps ACLs + and writes the bootstrap token back to Vault. + * agent-pre-populate: true - Run the Vault agent init container. + * agent-pre-populate-only: false - Also, run the Vault agent sidecar. + * agent-cache-enable: true - Enable the Agent cache listener. + * agent-cache-listener-port: 8200 - (optional) Listen on 127.0.0.1:8200. + * agent-enable-quit: true - Enable a "quit" endpoint. server-acl-init + tells the Vault agent to stop (without this the Job will not complete). + */}} + "vault.hashicorp.com/agent-pre-populate": "true" + "vault.hashicorp.com/agent-pre-populate-only": "false" + "vault.hashicorp.com/agent-cache-enable": "true" + "vault.hashicorp.com/agent-cache-listener-port": "8200" + "vault.hashicorp.com/agent-enable-quit": "true" + "vault.hashicorp.com/agent-inject": "true" + {{- if .Values.global.acls.partitionToken.secretName }} + {{- with .Values.global.acls.partitionToken }} + "vault.hashicorp.com/agent-inject-secret-partition-token": "{{ .secretName }}" + "vault.hashicorp.com/agent-inject-template-partition-token": {{ template "consul.vaultSecretTemplate" . }} + {{- end }} + {{- end }} + {{- if .Values.global.tls.enabled }} + "vault.hashicorp.com/agent-inject-secret-serverca.crt": {{ .Values.global.tls.caCert.secretName }} + "vault.hashicorp.com/agent-inject-template-serverca.crt": {{ template "consul.serverTLSCATemplate" . }} + {{- end }} + {{- if .Values.global.secretsBackend.vault.manageSystemACLsRole }} + "vault.hashicorp.com/role": {{ .Values.global.secretsBackend.vault.manageSystemACLsRole }} + {{- else if .Values.global.tls.enabled }} + "vault.hashicorp.com/role": {{ .Values.global.secretsBackend.vault.consulCARole }} + {{- end }} + {{- if and .Values.global.secretsBackend.vault.ca.secretName .Values.global.secretsBackend.vault.ca.secretKey }} + "vault.hashicorp.com/agent-extra-secret": "{{ .Values.global.secretsBackend.vault.ca.secretName }}" + "vault.hashicorp.com/ca-cert": "/vault/custom/{{ .Values.global.secretsBackend.vault.ca.secretKey }}" + {{- end }} + {{- if .Values.global.acls.replicationToken.secretName }} + "vault.hashicorp.com/agent-inject-secret-replication-token": "{{ .Values.global.acls.replicationToken.secretName }}" + "vault.hashicorp.com/agent-inject-template-replication-token": {{ template "consul.vaultReplicationTokenTemplate" . }} + {{- end }} + {{- if .Values.global.secretsBackend.vault.agentAnnotations }} + {{ tpl .Values.global.secretsBackend.vault.agentAnnotations . | nindent 8 | trim }} + {{- end }} + {{- if (and (.Values.global.secretsBackend.vault.vaultNamespace) (not (hasKey (default "" .Values.global.secretsBackend.vault.agentAnnotations | fromYaml) "vault.hashicorp.com/namespace")))}} + "vault.hashicorp.com/namespace": "{{ .Values.global.secretsBackend.vault.vaultNamespace }}" + {{- end }} + {{- end }} + spec: + restartPolicy: Never + serviceAccountName: {{ template "consul.fullname" . }}-server-acl-init + {{- if .Values.server.containerSecurityContext.aclInit }} + securityContext: + {{- toYaml .Values.server.containerSecurityContext.aclInit | nindent 8 }} + {{- end }} + {{- if (or .Values.global.tls.enabled .Values.global.acls.replicationToken.secretName .Values.global.acls.bootstrapToken.secretName) }} + volumes: + {{- if and .Values.global.tls.enabled (not .Values.global.secretsBackend.vault.enabled) }} + {{- if not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) }} + - name: consul-ca-cert + secret: + {{- if .Values.global.tls.caCert.secretName }} + secretName: {{ .Values.global.tls.caCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" . }}-ca-cert + {{- end }} + items: + - key: {{ default "tls.crt" .Values.global.tls.caCert.secretKey }} + path: tls.crt + {{- end }} + {{- end }} + {{- if and .Values.global.acls.replicationToken.secretName (not .Values.global.secretsBackend.vault.enabled) }} + - name: acl-replication-token + secret: + secretName: {{ .Values.global.acls.replicationToken.secretName }} + items: + - key: {{ .Values.global.acls.replicationToken.secretKey }} + path: acl-replication-token + {{- end }} + {{- end }} + containers: + - name: server-acl-init-job + image: {{ .Values.global.imageK8S }} + {{ template "consul.imagePullPolicy" . }} + {{- if not .Values.server.containerSecurityContext.aclInit }} + {{- include "consul.restrictedSecurityContext" . | nindent 8 }} + {{- end }} + env: + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + # Extract the Vault namespace from the Vault agent annotations. + {{- if .Values.global.secretsBackend.vault.enabled }} + {{- if and (.Values.global.secretsBackend.vault.agentAnnotations) (hasKey (default "" .Values.global.secretsBackend.vault.agentAnnotations | fromYaml) "vault.hashicorp.com/namespace") }} + - name: VAULT_NAMESPACE + value: {{ get (tpl .Values.global.secretsBackend.vault.agentAnnotations . | fromYaml) "vault.hashicorp.com/namespace" }} + {{- else if .Values.global.secretsBackend.vault.vaultNamespace }} + - name: VAULT_NAMESPACE + value: {{ .Values.global.secretsBackend.vault.vaultNamespace }} + {{- end }} + {{- end }} + {{- include "consul.consulK8sConsulServerEnvVars" . | nindent 8 }} + {{- if (or .Values.global.tls.enabled .Values.global.acls.replicationToken.secretName .Values.global.acls.bootstrapToken.secretName) }} + volumeMounts: + {{- if and .Values.global.tls.enabled (not .Values.global.secretsBackend.vault.enabled) }} + {{- if not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) }} + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- end }} + {{- if and .Values.global.acls.replicationToken.secretName (not .Values.global.secretsBackend.vault.enabled) }} + - name: acl-replication-token + mountPath: /consul/acl/tokens + readOnly: true + {{- end }} + {{- end }} + command: + - "/bin/sh" + - "-ec" + - | + CONSUL_FULLNAME="{{template "consul.fullname" . }}" + + exec consul-k8s-control-plane server-acl-init \ + -log-level={{ default .Values.global.logLevel .Values.global.acls.logLevel}} \ + -log-json={{ .Values.global.logJSON }} \ + -resource-prefix=${CONSUL_FULLNAME} \ + -k8s-namespace={{ .Release.Namespace }} \ + -set-server-tokens={{ $serverEnabled }} \ + {{- if .Values.global.secretsBackend.vault.enabled }} + -secrets-backend=vault \ + {{- else }} + -secrets-backend=kubernetes \ + {{- end }} + + {{- if (mustHas "resource-apis" .Values.global.experiments) }} + -enable-resource-apis=true \ + {{- end }} + + {{- if .Values.global.acls.bootstrapToken.secretName }} + -bootstrap-token-secret-name={{ .Values.global.acls.bootstrapToken.secretName }} \ + -bootstrap-token-secret-key={{ .Values.global.acls.bootstrapToken.secretKey }} \ + {{- end }} + + {{- if .Values.syncCatalog.enabled }} + -sync-catalog=true \ + {{- if .Values.syncCatalog.consulNodeName }} + -sync-consul-node-name={{ .Values.syncCatalog.consulNodeName }} \ + {{- end }} + {{- end }} + + {{- if .Values.global.peering.enabled }} + -enable-peering=true \ + {{- end }} + {{- if (or (and (ne (.Values.dns.enabled | toString) "-") .Values.dns.enabled) (and (eq (.Values.dns.enabled | toString) "-") .Values.connectInject.transparentProxy.defaultEnabled)) }} + -allow-dns=true \ + {{- end }} + + {{- if (or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled)) }} + -connect-inject=true \ + {{- end }} + {{- if and .Values.externalServers.enabled .Values.externalServers.k8sAuthMethodHost }} + -auth-method-host={{ .Values.externalServers.k8sAuthMethodHost }} \ + {{- end }} + + {{- if .Values.global.federation.k8sAuthMethodHost }} + -auth-method-host={{ .Values.global.federation.k8sAuthMethodHost }} \ + {{- end }} + + {{- if .Values.meshGateway.enabled }} + -mesh-gateway=true \ + {{- end }} + + {{- if .Values.ingressGateways.enabled }} + {{- if .Values.global.enableConsulNamespaces }} + {{- $root := . }} + {{- range .Values.ingressGateways.gateways }} + {{- if (or $root.Values.ingressGateways.defaults.consulNamespace .consulNamespace) }} + -ingress-gateway-name="{{ .name }}.{{ (default $root.Values.ingressGateways.defaults.consulNamespace .consulNamespace) }}" \ + {{- else }} + -ingress-gateway-name="{{ .name }}" \ + {{- end }} + {{- end }} + {{- else }} + {{- range .Values.ingressGateways.gateways }} + -ingress-gateway-name="{{ .name }}" \ + {{- end }} + {{- end }} + {{- end }} + + {{- if .Values.terminatingGateways.enabled }} + {{- if .Values.global.enableConsulNamespaces }} + {{- $root := . }} + {{- range .Values.terminatingGateways.gateways }} + {{- if (or $root.Values.terminatingGateways.defaults.consulNamespace .consulNamespace) }} + -terminating-gateway-name="{{ .name }}.{{ (default $root.Values.terminatingGateways.defaults.consulNamespace .consulNamespace) }}" \ + {{- else }} + -terminating-gateway-name="{{ .name }}" \ + {{- end }} + {{- end }} + {{- else }} + {{- range .Values.terminatingGateways.gateways }} + -terminating-gateway-name="{{ .name }}" \ + {{- end }} + {{- end }} + {{- end }} + + {{- if .Values.connectInject.aclBindingRuleSelector }} + -acl-binding-rule-selector={{ .Values.connectInject.aclBindingRuleSelector }} \ + {{- end }} + + {{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey) }} + -create-enterprise-license-token=true \ + {{- end }} + + {{- if (and (not .Values.global.metrics.datadog.dogstatsd.enabled) .Values.global.metrics.datadog.enabled .Values.global.acls.manageSystemACLs) }} + -create-dd-agent-token=true \ + {{- end }} + + {{- if .Values.server.snapshotAgent.enabled }} + -snapshot-agent=true \ + {{- end }} + + {{- if not (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled)) }} + -client=false \ + {{- end }} + + {{- if .Values.global.acls.createReplicationToken }} + -create-acl-replication-token=true \ + {{- end }} + + {{- if .Values.global.federation.enabled }} + -federation=true \ + {{- end }} + + {{- if .Values.global.acls.replicationToken.secretName }} + {{- if .Values.global.secretsBackend.vault.enabled }} + -acl-replication-token-file=/vault/secrets/replication-token \ + {{- else }} + -acl-replication-token-file=/consul/acl/tokens/acl-replication-token \ + {{- end }} + {{- end }} + {{- if and .Values.global.secretsBackend.vault.enabled .Values.global.acls.partitionToken.secretName }} + -partition-token-file=/vault/secrets/partition-token \ + {{- end }} + + {{- if .Values.global.enableConsulNamespaces }} + -enable-namespaces=true \ + {{- /* syncCatalog must be enabled to set sync flags */}} + {{- if (or (and (ne (.Values.syncCatalog.enabled | toString) "-") .Values.syncCatalog.enabled) (and (eq (.Values.syncCatalog.enabled | toString) "-") .Values.global.enabled)) }} + {{- if .Values.syncCatalog.consulNamespaces.consulDestinationNamespace }} + -consul-sync-destination-namespace={{ .Values.syncCatalog.consulNamespaces.consulDestinationNamespace }} \ + {{- end }} + {{- if .Values.syncCatalog.consulNamespaces.mirroringK8S }} + -enable-sync-k8s-namespace-mirroring=true \ + {{- if .Values.syncCatalog.consulNamespaces.mirroringK8SPrefix }} + -sync-k8s-namespace-mirroring-prefix={{ .Values.syncCatalog.consulNamespaces.mirroringK8SPrefix }} \ + {{- end }} + {{- end }} + {{- end }} + + {{- /* connectInject must be enabled to set inject flags */}} + {{- if (or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled)) }} + {{- if .Values.connectInject.consulNamespaces.consulDestinationNamespace }} + -consul-inject-destination-namespace={{ .Values.connectInject.consulNamespaces.consulDestinationNamespace }} \ + {{- end }} + {{- if .Values.connectInject.consulNamespaces.mirroringK8S }} + -enable-inject-k8s-namespace-mirroring=true \ + {{- if .Values.connectInject.consulNamespaces.mirroringK8SPrefix }} + -inject-k8s-namespace-mirroring-prefix={{ .Values.connectInject.consulNamespaces.mirroringK8SPrefix }} \ + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- if .Values.global.acls.resources }} + resources: + {{- toYaml .Values.global.acls.resources | nindent 10 }} + {{- end }} + {{- if .Values.global.acls.tolerations }} + tolerations: + {{ tpl .Values.global.acls.tolerations . | indent 8 | trim }} + {{- end }} + {{- if .Values.global.acls.nodeSelector }} + nodeSelector: + {{ tpl .Values.global.acls.nodeSelector . | indent 8 | trim }} + {{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/server-acl-init-podsecuritypolicy.yaml b/charts/hashicorp/consul/1.5.3/templates/server-acl-init-podsecuritypolicy.yaml new file mode 100644 index 0000000000..9bf93e2551 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/server-acl-init-podsecuritypolicy.yaml @@ -0,0 +1,41 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (or $serverEnabled .Values.externalServers.enabled) }} +{{- if .Values.global.acls.manageSystemACLs }} +{{- if .Values.global.enablePodSecurityPolicies }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-server-acl-init + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server-acl-init +spec: + privileged: false + # Allow core volume types. + volumes: + - 'secret' + - 'emptyDir' + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false + {{- end }} + {{- end }} + {{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/server-acl-init-role.yaml b/charts/hashicorp/consul/1.5.3/templates/server-acl-init-role.yaml new file mode 100644 index 0000000000..eb7b6a928e --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/server-acl-init-role.yaml @@ -0,0 +1,38 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (or $serverEnabled .Values.externalServers.enabled) }} +{{- if .Values.global.acls.manageSystemACLs }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" . }}-server-acl-init + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server-acl-init +rules: +- apiGroups: [ "" ] + resources: + - secrets + verbs: + - create + - get +- apiGroups: [ "" ] + resources: + - serviceaccounts + resourceNames: + - {{ template "consul.fullname" . }}-auth-method + verbs: + - get +{{- if .Values.global.enablePodSecurityPolicies }} +- apiGroups: [ "policy" ] + resources: [ "podsecuritypolicies" ] + resourceNames: + - {{ template "consul.fullname" . }}-server-acl-init + verbs: + - use +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/server-acl-init-rolebinding.yaml b/charts/hashicorp/consul/1.5.3/templates/server-acl-init-rolebinding.yaml new file mode 100644 index 0000000000..fda4726d9f --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/server-acl-init-rolebinding.yaml @@ -0,0 +1,23 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (or $serverEnabled .Values.externalServers.enabled) }} +{{- if .Values.global.acls.manageSystemACLs }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" . }}-server-acl-init + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server-acl-init +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" . }}-server-acl-init +subjects: + - kind: ServiceAccount + name: {{ template "consul.fullname" . }}-server-acl-init +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/server-acl-init-serviceaccount.yaml b/charts/hashicorp/consul/1.5.3/templates/server-acl-init-serviceaccount.yaml new file mode 100644 index 0000000000..c0e257de96 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/server-acl-init-serviceaccount.yaml @@ -0,0 +1,22 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (or $serverEnabled .Values.externalServers.enabled) }} +{{- if .Values.global.acls.manageSystemACLs }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-server-acl-init + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server-acl-init +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/server-clusterrole.yaml b/charts/hashicorp/consul/1.5.3/templates/server-clusterrole.yaml new file mode 100644 index 0000000000..c22f562264 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/server-clusterrole.yaml @@ -0,0 +1,16 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "consul.fullname" . }}-server + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server +rules: +- apiGroups: [""] + resources: ["nodes"] + verbs: + - get diff --git a/charts/hashicorp/consul/1.5.3/templates/server-clusterrolebinding.yaml b/charts/hashicorp/consul/1.5.3/templates/server-clusterrolebinding.yaml new file mode 100644 index 0000000000..854fda870e --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/server-clusterrolebinding.yaml @@ -0,0 +1,18 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "consul.fullname" . }}-server + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "consul.fullname" . }}-server +subjects: +- kind: ServiceAccount + name: {{ template "consul.fullname" . }}-server + namespace: {{ .Release.Namespace }} diff --git a/charts/hashicorp/consul/1.5.3/templates/server-config-configmap.yaml b/charts/hashicorp/consul/1.5.3/templates/server-config-configmap.yaml new file mode 100644 index 0000000000..8c74364a29 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/server-config-configmap.yaml @@ -0,0 +1,220 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (not (or (eq .Values.server.limits.requestLimits.mode "disabled") (eq .Values.server.limits.requestLimits.mode "permissive") (eq .Values.server.limits.requestLimits.mode "enforce"))) }}{{fail "server.limits.requestLimits.mode must be one of the following values: disabled, permissive, and enforce." }}{{ end -}} +{{- if and .Values.server.auditLogs.enabled (not .Values.global.acls.manageSystemACLs) }}{{fail "ACLs must be enabled inorder to configure audit logs"}}{{ end -}} +# StatefulSet to run the actual Consul server cluster. +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "consul.fullname" . }}-server-config + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server +data: + server.json: | + { + {{- if and .Values.global.secretsBackend.vault.enabled }} + "auto_reload_config": true, + {{- end }} + "bind_addr": "0.0.0.0", + "bootstrap_expect": {{ if .Values.server.bootstrapExpect }}{{ .Values.server.bootstrapExpect }}{{ else }}{{ .Values.server.replicas }}{{ end }}, + "client_addr": "0.0.0.0", + "connect": { + "enabled": {{ .Values.server.connect }} + }, + "datacenter": "{{ .Values.global.datacenter }}", + "data_dir": "/consul/data", + {{- if .Values.server.logLevel }} + "log_level": "{{ .Values.server.logLevel | upper }}", + {{- end }} + "enable_debug": {{ .Values.server.enableAgentDebug }}, + "domain": "{{ .Values.global.domain }}", + "limits": { + "request_limits": { + "mode": "{{ .Values.server.limits.requestLimits.mode }}", + "read_rate": {{ .Values.server.limits.requestLimits.readRate }}, + "write_rate": {{ .Values.server.limits.requestLimits.writeRate }} + } + }, + "ports": { + {{- if not .Values.global.tls.enabled }} + "grpc": 8502, + "grpc_tls": -1, + {{- end }} + {{- if .Values.global.tls.enabled }} + "grpc": -1, + "grpc_tls": 8502, + {{- end }} + "serf_lan": {{ .Values.server.ports.serflan.port }} + }, + "recursors": {{ .Values.global.recursors | toJson }}, + "retry_join": ["{{template "consul.fullname" . }}-server.{{ .Release.Namespace }}.svc:{{ .Values.server.ports.serflan.port }}"], + {{- if .Values.global.peering.enabled }} + "peering": { + "enabled": true + }, + {{- end }} + "server": true, + "leave_on_terminate": true, + "autopilot": { + "min_quorum": {{ template "consul.server.autopilotMinQuorum" . }}, + "disable_upgrade_migration": true + } + } + {{- $vaultConnectCAEnabled := and .Values.global.secretsBackend.vault.connectCA.address .Values.global.secretsBackend.vault.connectCA.rootPKIPath .Values.global.secretsBackend.vault.connectCA.intermediatePKIPath -}} + {{- if and .Values.global.secretsBackend.vault.enabled $vaultConnectCAEnabled }} + {{- with .Values.global.secretsBackend.vault }} + connect-ca-config.json: | + { + "connect": [ + { + "ca_config": [ + { + "address": "{{ .connectCA.address }}", + {{- if and .ca.secretName .ca.secretKey }} + "ca_file": "/consul/vault-ca/tls.crt", + {{- end }} + "intermediate_pki_path": "{{ .connectCA.intermediatePKIPath }}", + {{- if (and (.vaultNamespace) (not (contains "namespace" (default "" .connectCA.additionalConfig)))) }} + "namespace": "{{ .vaultNamespace }}", + {{- end }} + "root_pki_path": "{{ .connectCA.rootPKIPath }}", + "auth_method": { + "type": "kubernetes", + "mount_path": "{{ .connectCA.authMethodPath }}", + "params": { + "role": "{{ .consulServerRole }}" + } + } + } + ], + "ca_provider": "vault" + } + ] + } + {{- if .connectCA.additionalConfig }} + additional-connect-ca-config.json: | +{{ tpl .connectCA.additionalConfig $ | trimAll "\"" | indent 4 }} + {{- end }} + {{- end }} + {{- end }} + {{- if .Values.global.acls.manageSystemACLs }} + acl-config.json: |- + { + "acl": { + "enabled": true, + "default_policy": "deny", + "down_policy": "extend-cache", + {{- if (and .Values.global.acls.replicationToken.secretName .Values.global.acls.replicationToken.secretKey) }} + "enable_token_replication": true, + {{- end }} + "enable_token_persistence": true + } + } + {{- end }} + {{- if .Values.global.tls.enabled }} + tls-config.json: |- + { + "tls": { + {{- if .Values.global.tls.verify }} + "internal_rpc": { + "verify_incoming": true, + "verify_server_hostname": true + }, + "grpc": { + "verify_incoming": false + }, + {{- end }} + "defaults": { + {{- if .Values.global.tls.verify }} + "verify_outgoing": true, + {{- end }} + {{- if .Values.global.secretsBackend.vault.enabled }} + "ca_file": "/vault/secrets/serverca.crt", + "cert_file": "/vault/secrets/servercert.crt", + "key_file": "/vault/secrets/servercert.key" + {{- else }} + "ca_file": "/consul/tls/ca/tls.crt", + "cert_file": "/consul/tls/server/tls.crt", + "key_file": "/consul/tls/server/tls.key" + {{- end }} + } + }, + {{- if .Values.global.tls.enableAutoEncrypt }} + "auto_encrypt": { + "allow_tls": true + }, + {{- end }} + "ports": { + {{- if .Values.global.tls.httpsOnly }} + "http": -1, + {{- end }} + "https": 8501 + } + } + {{- end }} + {{- if .Values.ui.enabled }} + ui-config.json: |- + { + "ui_config": { + {{- if (or (eq "true" (.Values.ui.metrics.enabled | toString) ) (and .Values.global.metrics.enabled (eq "-" (.Values.ui.metrics.enabled | toString)))) }} + "metrics_provider": "{{ .Values.ui.metrics.provider }}", + "metrics_proxy": { + "base_url": "{{ .Values.ui.metrics.baseURL }}" + }, + {{- end }} + {{- if .Values.ui.dashboardURLTemplates.service }} + "dashboard_url_templates": { + "service": "{{ .Values.ui.dashboardURLTemplates.service }}" + }, + {{- end }} + "enabled": true + } + } + {{- end }} + central-config.json: |- + { + "enable_central_service_config": true + } + {{- if .Values.global.federation.enabled }} + federation-config.json: |- + { + "primary_datacenter": "{{ .Values.global.federation.primaryDatacenter }}", + "primary_gateways": {{ .Values.global.federation.primaryGateways | toJson }}, + "connect": { + "enable_mesh_gateway_wan_federation": true + } + } + {{- end }} + {{- if (and .Values.global.metrics.enabled .Values.global.metrics.enableAgentMetrics) }} + telemetry-config.json: |- + { + "telemetry": { + "prometheus_retention_time": "{{ .Values.global.metrics.agentMetricsRetentionTime }}", + "disable_hostname": {{ .Values.global.metrics.disableAgentHostName }},{{ template "consul.prefixFilter" . }} + "enable_host_metrics": {{ .Values.global.metrics.enableHostMetrics }}{{- if .Values.global.metrics.datadog.dogstatsd.enabled }},{{ template "consul.dogstatsdAaddressInfo" . }} + {{- if .Values.global.metrics.datadog.dogstatsd.enabled }} + "dogstatsd_tags": {{ .Values.global.metrics.datadog.dogstatsd.dogstatsdTags | toJson }} + {{- end }} + {{- end }} + } + } + {{- end }} + {{- if and .Values.server.auditLogs.enabled .Values.global.acls.manageSystemACLs }} + audit-logging.json: |- + { + "audit": { + "enabled": true, + "sink": { + {{- range $index, $element := .Values.server.auditLogs.sinks }} + {{- if ne $index 0 }},{{end}} + "{{ get $element "name" }}": {{ omit $element "name" | toJson }} + {{- end }} + } + } + } + {{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/server-disruptionbudget.yaml b/charts/hashicorp/consul/1.5.3/templates/server-disruptionbudget.yaml new file mode 100644 index 0000000000..56805edc2a --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/server-disruptionbudget.yaml @@ -0,0 +1,26 @@ +{{- if (and .Values.server.disruptionBudget.enabled (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled))) }} +# PodDisruptionBudget to prevent degrading the server cluster through +# voluntary cluster changes. +{{- if .Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" }} +apiVersion: policy/v1 +{{- else }} +apiVersion: policy/v1beta1 +{{- end }} +kind: PodDisruptionBudget +metadata: + name: {{ template "consul.fullname" . }}-server + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server +spec: + maxUnavailable: {{ template "consul.server.pdb.maxUnavailable" . }} + selector: + matchLabels: + app: {{ template "consul.name" . }} + release: "{{ .Release.Name }}" + component: server +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/server-podsecuritypolicy.yaml b/charts/hashicorp/consul/1.5.3/templates/server-podsecuritypolicy.yaml new file mode 100644 index 0000000000..09e8d75bd1 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/server-podsecuritypolicy.yaml @@ -0,0 +1,53 @@ +{{- if (and .Values.global.enablePodSecurityPolicies (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled))) }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-server + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + # Allow core volume types. + volumes: + - 'configMap' + - 'emptyDir' + - 'projected' + - 'secret' + - 'downwardAPI' + - 'persistentVolumeClaim' + hostNetwork: false + hostPorts: + {{- if .Values.server.exposeGossipAndRPCPorts }} + - min: 8300 + max: 8300 + - min: {{ .Values.server.ports.serflan.port }} + max: {{ .Values.server.ports.serflan.port }} + - min: 8302 + max: 8302 + - min: 8502 + max: 8502 + {{- end }} + hostIPC: false + hostPID: false + runAsUser: + # Require the container to run without root privileges. + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/server-role.yaml b/charts/hashicorp/consul/1.5.3/templates/server-role.yaml new file mode 100644 index 0000000000..202518bf67 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/server-role.yaml @@ -0,0 +1,34 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" . }}-server + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server +{{- if (or (and .Values.global.openshift.enabled .Values.server.exposeGossipAndRPCPorts) .Values.global.enablePodSecurityPolicies) }} +rules: +{{- if .Values.global.enablePodSecurityPolicies }} +- apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + resourceNames: + - {{ template "consul.fullname" . }}-server + verbs: + - use +{{- end }} +{{- if (and .Values.global.openshift.enabled .Values.server.exposeGossipAndRPCPorts ) }} +- apiGroups: ["security.openshift.io"] + resources: ["securitycontextconstraints"] + resourceNames: + - {{ template "consul.fullname" . }}-server + verbs: + - use +{{- end }} +{{- else}} +rules: [] +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/server-rolebinding.yaml b/charts/hashicorp/consul/1.5.3/templates/server-rolebinding.yaml new file mode 100644 index 0000000000..8ab705ddbc --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/server-rolebinding.yaml @@ -0,0 +1,20 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" . }}-server + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" . }}-server +subjects: + - kind: ServiceAccount + name: {{ template "consul.fullname" . }}-server +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/server-securitycontextconstraints.yaml b/charts/hashicorp/consul/1.5.3/templates/server-securitycontextconstraints.yaml new file mode 100644 index 0000000000..8edd784ea7 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/server-securitycontextconstraints.yaml @@ -0,0 +1,49 @@ +{{- if (and .Values.global.openshift.enabled .Values.server.exposeGossipAndRPCPorts (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled))) }} +apiVersion: security.openshift.io/v1 +kind: SecurityContextConstraints +metadata: + name: {{ template "consul.fullname" . }}-server + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server + annotations: + kubernetes.io/description: {{ template "consul.fullname" . }}-server are the security context constraints required + to run the consul server. +allowHostPorts: true +allowHostDirVolumePlugin: false +allowHostIPC: false +allowHostPID: false +allowHostNetwork: false +allowPrivilegeEscalation: false +allowPrivilegedContainer: false +allowedCapabilities: null +defaultAddCapabilities: null +fsGroup: + type: MustRunAs +groups: [] +priority: null +readOnlyRootFilesystem: false +requiredDropCapabilities: +- KILL +- MKNOD +- SETUID +- SETGID +runAsUser: + type: MustRunAsRange +seLinuxContext: + type: MustRunAs +supplementalGroups: + type: MustRunAs +users: [] +volumes: +- configMap +- downwardAPI +- emptyDir +- persistentVolumeClaim +- projected +- secret +{{- end -}} diff --git a/charts/hashicorp/consul/1.5.3/templates/server-service.yaml b/charts/hashicorp/consul/1.5.3/templates/server-service.yaml new file mode 100644 index 0000000000..a392f0e76b --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/server-service.yaml @@ -0,0 +1,72 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +# Headless service for Consul server DNS entries. This service should only +# point to Consul servers. For access to an agent, one should assume that +# the agent is installed locally on the node and the NODE_IP should be used. +# If the node can't run a Consul agent, then this service can be used to +# communicate directly to a server agent. +apiVersion: v1 +kind: Service +metadata: + name: {{ template "consul.fullname" . }}-server + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server + annotations: + {{- if .Values.server.service.annotations }} + {{ tpl .Values.server.service.annotations . | nindent 4 | trim }} + {{- end }} +spec: + clusterIP: None + # We want the servers to become available even if they're not ready + # since this DNS is also used for join operations. + publishNotReadyAddresses: true + ports: + {{- if (or (not .Values.global.tls.enabled) (not .Values.global.tls.httpsOnly)) }} + - name: http + port: 8500 + targetPort: 8500 + {{- end }} + {{- if .Values.global.tls.enabled }} + - name: https + port: 8501 + targetPort: 8501 + {{- end }} + - name: grpc + port: 8502 + targetPort: 8502 + - name: serflan-tcp + protocol: "TCP" + port: 8301 + targetPort: 8301 + - name: serflan-udp + protocol: "UDP" + port: 8301 + targetPort: 8301 + - name: serfwan-tcp + protocol: "TCP" + port: 8302 + targetPort: 8302 + - name: serfwan-udp + protocol: "UDP" + port: 8302 + targetPort: 8302 + - name: server + port: 8300 + targetPort: 8300 + - name: dns-tcp + protocol: "TCP" + port: 8600 + targetPort: dns-tcp + - name: dns-udp + protocol: "UDP" + port: 8600 + targetPort: dns-udp + selector: + app: {{ template "consul.name" . }} + release: "{{ .Release.Name }}" + component: server +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/server-serviceaccount.yaml b/charts/hashicorp/consul/1.5.3/templates/server-serviceaccount.yaml new file mode 100644 index 0000000000..a1617975ae --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/server-serviceaccount.yaml @@ -0,0 +1,23 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-server + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server + {{- if .Values.server.serviceAccount.annotations }} + annotations: + {{ tpl .Values.server.serviceAccount.annotations . | nindent 4 | trim }} + {{- end }} +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/server-snapshot-agent-configmap.yaml b/charts/hashicorp/consul/1.5.3/templates/server-snapshot-agent-configmap.yaml new file mode 100644 index 0000000000..da68d1509c --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/server-snapshot-agent-configmap.yaml @@ -0,0 +1,24 @@ +{{- if .Values.server.snapshotAgent.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "consul.fullname" . }}-snapshot-agent-config + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server +data: + snapshot-login.json: | + { + "snapshot_agent": { + "login": { + "auth_method": "{{ template "consul.fullname" . }}-k8s-component-auth-method", + "bearer_token_file": "/var/run/secrets/kubernetes.io/serviceaccount/token", + "meta": {"component": "snapshot-agent"} + } + } + } +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/server-statefulset.yaml b/charts/hashicorp/consul/1.5.3/templates/server-statefulset.yaml new file mode 100644 index 0000000000..f8cb9b4def --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/server-statefulset.yaml @@ -0,0 +1,757 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if and .Values.global.federation.enabled .Values.global.adminPartitions.enabled }}{{ fail "If global.federation.enabled is true, global.adminPartitions.enabled must be false because they are mutually exclusive" }}{{ end }} +{{- if and .Values.global.federation.enabled (not .Values.global.tls.enabled) }}{{ fail "If global.federation.enabled is true, global.tls.enabled must be true because federation is only supported with TLS enabled" }}{{ end }} +{{- if and .Values.global.federation.enabled (not .Values.meshGateway.enabled) }}{{ fail "If global.federation.enabled is true, meshGateway.enabled must be true because mesh gateways are required for federation" }}{{ end }} +{{- if and .Values.server.serverCert.secretName (not .Values.global.tls.caCert.secretName) }}{{ fail "If server.serverCert.secretName is provided, global.tls.caCert must also be provided" }}{{ end }} +{{- if .Values.server.disableFsGroupSecurityContext }}{{ fail "server.disableFsGroupSecurityContext has been removed. Please use global.openshift.enabled instead." }}{{ end }} +{{- if .Values.server.bootstrapExpect }}{{ if lt (int .Values.server.bootstrapExpect) (int .Values.server.replicas) }}{{ fail "server.bootstrapExpect cannot be less than server.replicas" }}{{ end }}{{ end }} +{{- if (and .Values.global.gossipEncryption.secretName (not .Values.global.gossipEncryption.secretKey)) }}{{fail "gossipEncryption.secretKey and secretName must both be specified." }}{{ end -}} +{{- if (and (not .Values.global.gossipEncryption.secretName) .Values.global.gossipEncryption.secretKey) }}{{fail "gossipEncryption.secretKey and secretName must both be specified." }}{{ end -}} +{{- if (and .Values.global.secretsBackend.vault.enabled (not .Values.global.secretsBackend.vault.consulServerRole)) }}{{ fail "global.secretsBackend.vault.consulServerRole must be provided if global.secretsBackend.vault.enabled=true." }}{{ end -}} +{{- if (and .Values.server.serverCert.secretName (not .Values.global.tls.caCert.secretName)) }}{{ fail "If server.serverCert.secretName is provided, global.tls.caCert.secretName must also be provided" }}{{ end }} +{{- if (and (and .Values.global.secretsBackend.vault.enabled .Values.global.tls.enabled) (not .Values.global.tls.caCert.secretName)) }}{{ fail "global.tls.caCert.secretName must be provided if global.tls.enabled=true and global.secretsBackend.vault.enabled=true." }}{{ end -}} +{{- if (and .Values.global.enterpriseLicense.secretName (not .Values.global.enterpriseLicense.secretKey)) }}{{fail "enterpriseLicense.secretKey and secretName must both be specified." }}{{ end -}} +{{- if (and (not .Values.global.enterpriseLicense.secretName) .Values.global.enterpriseLicense.secretKey) }}{{fail "enterpriseLicense.secretKey and secretName must both be specified." }}{{ end -}} +{{- if (and .Values.global.acls.bootstrapToken.secretName (not .Values.global.acls.bootstrapToken.secretKey)) }}{{fail "both global.acls.bootstrapToken.secretKey and global.acls.bootstrapToken.secretName must be set if one of them is provided." }}{{ end -}} +{{- if (and (not .Values.global.acls.bootstrapToken.secretName) .Values.global.acls.bootstrapToken.secretKey) }}{{fail "both global.acls.bootstrapToken.secretKey and global.acls.bootstrapToken.secretName must be set if one of them is provided." }}{{ end -}} +{{- if .Values.server.snapshotAgent.enabled -}} +{{- if or (and .Values.server.snapshotAgent.configSecret.secretName (not .Values.server.snapshotAgent.configSecret.secretKey)) (and (not .Values.server.snapshotAgent.configSecret.secretName) .Values.server.snapshotAgent.configSecret.secretKey) }}{{fail "server.snapshotAgent.configSecret.secretKey and server.snapshotAgent.configSecret.secretName must both be specified." }}{{ end -}} +{{- end -}} +{{ template "consul.validateRequiredCloudSecretsExist" . }} +{{ template "consul.validateCloudSecretKeys" . }} +{{ template "consul.validateMetricsConfig" . }} +{{ template "consul.validateDatadogConfiguration" . }} +{{ template "consul.validateExtraConfig" . }} +# StatefulSet to run the actual Consul server cluster. +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ template "consul.fullname" . }}-server + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} +spec: + serviceName: {{ template "consul.fullname" . }}-server + podManagementPolicy: Parallel + replicas: {{ .Values.server.replicas }} + {{- if (gt (int .Values.server.updatePartition) 0) }} + updateStrategy: + type: RollingUpdate + rollingUpdate: + partition: {{ .Values.server.updatePartition }} + {{- end }} + {{- if and (semverCompare ">= 1.23-0" .Capabilities.KubeVersion.Version) (.Values.server.persistentVolumeClaimRetentionPolicy) }} + persistentVolumeClaimRetentionPolicy: {{ toYaml .Values.server.persistentVolumeClaimRetentionPolicy | nindent 4 }} + {{- end }} + selector: + matchLabels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: server + hasDNS: "true" + template: + metadata: + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: server + hasDNS: "true" + {{- if .Values.global.metrics.datadog.enabled }} + "tags.datadoghq.com/version": {{ template "consul.versionInfo" . }} + "tags.datadoghq.com/env": {{ template "consul.name" . }} + "tags.datadoghq.com/service": "consul-server" + {{- end }} + {{- if .Values.server.extraLabels }} + {{- toYaml .Values.server.extraLabels | nindent 8 }} + {{- end }} + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} + annotations: + {{- if .Values.global.secretsBackend.vault.enabled }} + "vault.hashicorp.com/agent-inject": "true" + "vault.hashicorp.com/role": "{{ .Values.global.secretsBackend.vault.consulServerRole }}" + {{- if and .Values.global.secretsBackend.vault.ca.secretName .Values.global.secretsBackend.vault.ca.secretKey }} + "vault.hashicorp.com/agent-extra-secret": {{ .Values.global.secretsBackend.vault.ca.secretName }} + "vault.hashicorp.com/ca-cert": /vault/custom/{{ .Values.global.secretsBackend.vault.ca.secretKey }} + {{- end }} + {{- if .Values.global.gossipEncryption.secretName }} + {{- with .Values.global.gossipEncryption }} + "vault.hashicorp.com/agent-inject-secret-gossip.txt": "{{ .secretName }}" + "vault.hashicorp.com/agent-inject-template-gossip.txt": {{ template "consul.vaultSecretTemplate" . }} + {{- end }} + {{- end }} + {{- if .Values.server.serverCert.secretName }} + "vault.hashicorp.com/agent-inject-secret-servercert.crt": {{ .Values.server.serverCert.secretName }} + "vault.hashicorp.com/agent-inject-template-servercert.crt": {{ include "consul.serverTLSCertTemplate" . }} + "vault.hashicorp.com/agent-inject-secret-servercert.key": {{ .Values.server.serverCert.secretName }} + "vault.hashicorp.com/agent-inject-template-servercert.key": {{ include "consul.serverTLSKeyTemplate" . }} + "vault.hashicorp.com/agent-inject-secret-serverca.crt": {{ .Values.global.tls.caCert.secretName }} + "vault.hashicorp.com/agent-inject-template-serverca.crt": {{ include "consul.serverTLSCATemplate" . }} + {{- end }} + {{- if (and .Values.global.acls.replicationToken.secretName (not .Values.global.acls.createReplicationToken)) }} + "vault.hashicorp.com/agent-inject-secret-replication-token-config.hcl": "{{ .Values.global.acls.replicationToken.secretName }}" + "vault.hashicorp.com/agent-inject-template-replication-token-config.hcl": {{ template "consul.vaultReplicationTokenConfigTemplate" . }} + {{- end }} + {{- if (and .Values.global.acls.manageSystemACLs .Values.global.acls.bootstrapToken.secretName) }} + "vault.hashicorp.com/agent-inject-secret-bootstrap-token-config.hcl": "{{ .Values.global.acls.bootstrapToken.secretName }}" + "vault.hashicorp.com/agent-inject-template-bootstrap-token-config.hcl": {{ template "consul.vaultBootstrapTokenConfigTemplate" . }} + {{- end }} + {{- if .Values.global.secretsBackend.vault.agentAnnotations }} + {{ tpl .Values.global.secretsBackend.vault.agentAnnotations . | nindent 8 | trim }} + {{- end }} + {{- if (and (.Values.global.secretsBackend.vault.vaultNamespace) (not (hasKey (default "" .Values.global.secretsBackend.vault.agentAnnotations | fromYaml) "vault.hashicorp.com/namespace")))}} + "vault.hashicorp.com/namespace": "{{ .Values.global.secretsBackend.vault.vaultNamespace }}" + {{- end }} + {{- if .Values.global.enterpriseLicense.secretName }} + {{- with .Values.global.enterpriseLicense }} + "vault.hashicorp.com/agent-inject-secret-enterpriselicense.txt": "{{ .secretName }}" + "vault.hashicorp.com/agent-inject-template-enterpriselicense.txt": {{ template "consul.vaultSecretTemplate" . }} + {{- end }} + {{- end }} + {{- if .Values.server.snapshotAgent.configSecret.secretName }} + {{- with .Values.server.snapshotAgent.configSecret }} + "vault.hashicorp.com/agent-inject-secret-snapshot-agent-config.json": "{{ .secretName }}" + "vault.hashicorp.com/agent-inject-template-snapshot-agent-config.json": {{ template "consul.vaultSecretTemplate" . }} + {{- end }} + {{- end }} + {{- end }} + "consul.hashicorp.com/connect-inject": "false" + "consul.hashicorp.com/mesh-inject": "false" + "consul.hashicorp.com/config-checksum": {{ print (include (print $.Template.BasePath "/server-config-configmap.yaml") .) (include (print $.Template.BasePath "/server-tmp-extra-config-configmap.yaml") .) | sha256sum }} + {{- if .Values.server.annotations }} + {{- tpl .Values.server.annotations . | nindent 8 }} + {{- end }} + {{- if (and .Values.global.metrics.enabled .Values.global.metrics.enableAgentMetrics) }} + {{- if (or (not .Values.global.metrics.datadog.enabled) (and .Values.global.metrics.datadog.enabled (.Values.global.metrics.datadog.dogstatsd.enabled))) }} + "prometheus.io/scrape": "true" + {{- if not (hasKey (default "" .Values.server.annotations | fromYaml) "prometheus.io/path")}} + "prometheus.io/path": "/v1/agent/metrics" + {{- end }} + {{- if .Values.global.tls.enabled }} + "prometheus.io/port": "8501" + "prometheus.io/scheme": "https" + {{- else }} + "prometheus.io/port": "8500" + "prometheus.io/scheme": "http" + {{- end }} + {{- end }} + {{- if .Values.global.metrics.datadog.enabled }} + "ad.datadoghq.com/tolerate-unready": "true" + "ad.datadoghq.com/consul.logs": {{ .Values.global.metrics.datadog.dogstatsd.dogstatsdTags | toJson | replace "[" "[{" | replace "]" "}]" | replace ":" "\": \"" | join "\",\"" | squote }} + {{- if .Values.global.metrics.datadog.openMetricsPrometheus.enabled }} + "ad.datadoghq.com/consul.checks": | + { + "openmetrics": { + "init_config": {}, + "instances": [ + { + {{- if .Values.global.tls.enabled }} + "openmetrics_endpoint": "https://{{ template "consul.fullname" . }}-server.{{ .Release.Namespace }}.svc:8501/v1/agent/metrics?format=prometheus", + "tls_cert": "/etc/datadog-agent/conf.d/consul.d/certs/tls.crt", + "tls_private_key": "/etc/datadog-agent/conf.d/consul.d/certs/tls.key", + "tls_ca_cert": "/etc/datadog-agent/conf.d/consul.d/ca/tls.crt", + {{- else }} + "openmetrics_endpoint": "http://{{ template "consul.fullname" . }}-server.{{ .Release.Namespace }}.svc:8500/v1/agent/metrics?format=prometheus", + {{- end }} + {{- if ( .Values.global.acls.manageSystemACLs) }} + "headers": { + "X-Consul-Token": "ENC[k8s_secret@{{ .Release.Namespace }}/{{ .Release.Namespace }}-datadog-agent-metrics-acl-token/token]" + }, + {{- end }} + "namespace": "{{ .Release.Namespace }}", + "metrics": [ ".*" ] + } + ] + } + } + {{- else if (not .Values.global.metrics.datadog.dogstatsd.enabled) }} + "ad.datadoghq.com/consul.checks": | + { + "consul": { + "init_config": {}, + "instances": [ + { + {{- if .Values.global.tls.enabled }} + "url": "https://{{ template "consul.fullname" . }}-server.{{ .Release.Namespace }}.svc:8501", + "tls_cert": "/etc/datadog-agent/conf.d/consul.d/certs/tls.crt", + "tls_private_key": "/etc/datadog-agent/conf.d/consul.d/certs/tls.key", + "tls_ca_cert": "/etc/datadog-agent/conf.d/consul.d/ca/tls.crt", + {{- else }} + "url": "http://{{ template "consul.fullname" . }}-server.{{ .Release.Namespace }}.svc:8500", + {{- end }} + "use_prometheus_endpoint": true, + {{- if ( .Values.global.acls.manageSystemACLs) }} + "acl_token": "ENC[k8s_secret@{{ .Release.Namespace }}/{{ .Release.Namespace }}-datadog-agent-metrics-acl-token/token]", + {{- end }} + "new_leader_checks": true, + "network_latency_checks": true, + "catalog_checks": true, + "auth_type": "basic" + } + ] + } + } + {{- else }} + "ad.datadoghq.com/consul.metrics_exclude": "true" + {{- end }} + {{- end }} + {{- end }} + spec: + {{- if .Values.server.affinity }} + affinity: + {{ tpl .Values.server.affinity . | nindent 8 | trim }} + {{- end }} + {{- if .Values.server.tolerations }} + tolerations: + {{ tpl .Values.server.tolerations . | nindent 8 | trim }} + {{- end }} + {{- if .Values.server.topologySpreadConstraints }} + topologySpreadConstraints: + {{ tpl .Values.server.topologySpreadConstraints . | nindent 8 | trim }} + {{- end }} + terminationGracePeriodSeconds: 30 + serviceAccountName: {{ template "consul.fullname" . }}-server + {{- if not .Values.global.openshift.enabled }} + securityContext: + {{- toYaml .Values.server.securityContext | nindent 8 }} + {{- end }} + volumes: + - name: tmp + emptyDir: {} + - name: config + configMap: + name: {{ template "consul.fullname" . }}-server-config + - name: extra-config + emptyDir: {} + - name: tmp-extra-config + configMap: + name: {{ template "consul.fullname" . }}-server-tmp-extra-config + {{- if (and .Values.global.tls.enabled (not .Values.global.secretsBackend.vault.enabled)) }} + - name: consul-ca-cert + secret: + {{- if .Values.global.tls.caCert.secretName }} + secretName: {{ .Values.global.tls.caCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" . }}-ca-cert + {{- end }} + items: + - key: {{ default "tls.crt" .Values.global.tls.caCert.secretKey }} + path: tls.crt + - name: consul-server-cert + secret: + {{- if .Values.server.serverCert.secretName }} + secretName: {{ .Values.server.serverCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" . }}-server-cert + {{- end }} + {{- end }} + {{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.enableLicenseAutoload (not .Values.global.secretsBackend.vault.enabled)) }} + - name: consul-license + secret: + secretName: {{ .Values.global.enterpriseLicense.secretName }} + {{- end }} + {{- if and .Values.global.secretsBackend.vault.ca.secretName .Values.global.secretsBackend.vault.ca.secretKey }} + - name: vault-ca + secret: + secretName: {{ .Values.global.secretsBackend.vault.ca.secretName }} + items: + - key: {{ .Values.global.secretsBackend.vault.ca.secretKey }} + path: tls.crt + {{- end }} + {{- if .Values.server.snapshotAgent.enabled }} + {{- if .Values.global.acls.manageSystemACLs }} + - name: snapshot-agent-config + configMap: + name: {{ template "consul.fullname" . }}-snapshot-agent-config + {{- end }} + {{- if (and .Values.server.snapshotAgent.configSecret.secretName .Values.server.snapshotAgent.configSecret.secretKey (not .Values.global.secretsBackend.vault.enabled)) }} + - name: snapshot-agent-user-config + secret: + secretName: {{ .Values.server.snapshotAgent.configSecret.secretName }} + items: + - key: {{ .Values.server.snapshotAgent.configSecret.secretKey }} + path: snapshot-config.json + {{- end }} + {{- if .Values.server.snapshotAgent.caCert }} + - name: extra-ssl-certs + emptyDir: + medium: "Memory" + {{- end }} + {{- end }} + {{- if .Values.global.trustedCAs }} + - name: trusted-cas + emptyDir: + medium: "Memory" + {{- end }} + {{- if and .Values.global.metrics.datadog.enabled .Values.global.metrics.datadog.dogstatsd.enabled (eq .Values.global.metrics.datadog.dogstatsd.socketTransportType "UDS" ) }} + - name: dsdsocket + hostPath: + path: {{ dir .Values.global.metrics.datadog.dogstatsd.dogstatsdAddr | trimAll "\"" }} + type: DirectoryOrCreate + {{- end }} + {{- range .Values.server.extraVolumes }} + - name: userconfig-{{ .name }} + {{ .type }}: + {{- if (eq .type "configMap") }} + name: {{ .name }} + {{- else if (eq .type "secret") }} + secretName: {{ .name }} + {{- end }} + {{- with .items }} + items: + {{- range . }} + - key: {{.key}} + path: {{.path}} + {{- end }} + {{- end }} + {{- end }} + {{- if .Values.server.priorityClassName }} + priorityClassName: {{ .Values.server.priorityClassName | quote }} + {{- end }} + initContainers: + - name: locality-init + image: {{ .Values.global.imageK8S }} + {{ template "consul.imagePullPolicy" . }} + env: + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + command: + - "/bin/sh" + - "-ec" + - | + exec consul-k8s-control-plane fetch-server-region -node-name "$NODE_NAME" -output-file /consul/extra-config/locality.json + volumeMounts: + - name: extra-config + mountPath: /consul/extra-config + {{- include "consul.restrictedSecurityContext" . | nindent 8 }} + containers: + - name: consul + image: "{{ default .Values.global.image .Values.server.image | trimPrefix "\"" | trimSuffix "\"" }}" + {{ template "consul.imagePullPolicy" . }} + imagePullPolicy: {{ .Values.global.imagePullPolicy }} + env: + - name: ADVERTISE_IP + valueFrom: + fieldRef: + {{- if .Values.server.exposeGossipAndRPCPorts }} + {{- /* Server gossip and RPC ports will be exposed as a hostPort + on the hostIP, so they need to advertise their host ip + instead of their pod ip. This is to support external client + agents. */}} + fieldPath: status.hostIP + {{- else }} + fieldPath: status.podIP + {{- end }} + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: CONSUL_DISABLE_PERM_MGMT + value: "true" + {{- if (or .Values.global.gossipEncryption.autoGenerate (and .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey)) }} + {{- if not .Values.global.secretsBackend.vault.enabled }} + - name: GOSSIP_KEY + valueFrom: + secretKeyRef: + {{- if .Values.global.gossipEncryption.autoGenerate }} + name: {{ template "consul.fullname" . }}-gossip-encryption-key + key: key + {{- else if (and .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey) }} + name: {{ .Values.global.gossipEncryption.secretName }} + key: {{ .Values.global.gossipEncryption.secretKey }} + {{- end }} + {{- end }} + {{- end }} + {{- if .Values.global.tls.enabled }} + - name: CONSUL_HTTP_ADDR + value: https://localhost:8501 + - name: CONSUL_CACERT + {{- if .Values.global.secretsBackend.vault.enabled }} + value: /vault/secrets/serverca.crt + {{- else }} + value: /consul/tls/ca/tls.crt + {{- end }} + {{- end }} + {{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.enableLicenseAutoload) }} + - name: CONSUL_LICENSE_PATH + {{- if .Values.global.secretsBackend.vault.enabled }} + value: /vault/secrets/enterpriselicense.txt + {{- else }} + value: /consul/license/{{ .Values.global.enterpriseLicense.secretKey }} + {{- end }} + {{- end }} + {{- if and (not .Values.global.secretsBackend.vault.enabled) .Values.global.acls.bootstrapToken.secretName }} + - name: ACL_BOOTSTRAP_TOKEN + valueFrom: + secretKeyRef: + name: {{ .Values.global.acls.bootstrapToken.secretName | quote }} + key: {{ .Values.global.acls.bootstrapToken.secretKey | quote }} + {{- end }} + {{- if (and .Values.global.acls.replicationToken.secretName .Values.global.acls.replicationToken.secretKey (not .Values.global.secretsBackend.vault.enabled)) }} + - name: ACL_REPLICATION_TOKEN + valueFrom: + secretKeyRef: + name: {{ .Values.global.acls.replicationToken.secretName | quote }} + key: {{ .Values.global.acls.replicationToken.secretKey | quote }} + {{- end }} + {{- if .Values.global.cloud.enabled}} + # These are mounted as secrets so that the consul server agent can use them. + # - the hcp-go-sdk in consul agent will already look for HCP_CLIENT_ID, HCP_CLIENT_SECRET, HCP_AUTH_URL, + # HCP_SCADA_ADDRESS, and HCP_API_HOST. so nothing more needs to be done. + # - HCP_RESOURCE_ID is created for use in the + # `-hcl="cloud { resource_id = \"${HCP_RESOURCE_ID}\" }"` logic in the command below. + {{- if .Values.global.cloud.clientId.secretName }} + - name: HCP_CLIENT_ID + valueFrom: + secretKeyRef: + name: {{ .Values.global.cloud.clientId.secretName }} + key: {{ .Values.global.cloud.clientId.secretKey }} + {{- end }} + {{- if .Values.global.cloud.clientSecret.secretName }} + - name: HCP_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: {{ .Values.global.cloud.clientSecret.secretName }} + key: {{ .Values.global.cloud.clientSecret.secretKey }} + {{- end}} + {{- if .Values.global.cloud.resourceId.secretName }} + - name: HCP_RESOURCE_ID + valueFrom: + secretKeyRef: + name: {{ .Values.global.cloud.resourceId.secretName }} + key: {{ .Values.global.cloud.resourceId.secretKey }} + {{- end }} + {{- if .Values.global.cloud.authUrl.secretName }} + - name: HCP_AUTH_URL + valueFrom: + secretKeyRef: + name: {{ .Values.global.cloud.authUrl.secretName }} + key: {{ .Values.global.cloud.authUrl.secretKey }} + {{- end}} + {{- if .Values.global.cloud.apiHost.secretName }} + - name: HCP_API_HOST + valueFrom: + secretKeyRef: + name: {{ .Values.global.cloud.apiHost.secretName }} + key: {{ .Values.global.cloud.apiHost.secretKey }} + {{- end}} + {{- if .Values.global.cloud.scadaAddress.secretName }} + - name: HCP_SCADA_ADDRESS + valueFrom: + secretKeyRef: + name: {{ .Values.global.cloud.scadaAddress.secretName }} + key: {{ .Values.global.cloud.scadaAddress.secretKey }} + {{- end}} + {{- end }} + {{- if .Values.global.trustedCAs }} + - name: SSL_CERT_DIR + value: "/etc/ssl/certs:/trusted-cas" + {{- end }} + {{- include "consul.extraEnvironmentVars" .Values.server | nindent 12 }} + command: + - "/bin/sh" + - "-ec" + - | + {{- if .Values.global.trustedCAs }} + {{- range $i, $cert := .Values.global.trustedCAs }} + cat < /trusted-cas/custom-ca-{{$i}}.pem + {{- $cert | nindent 14 }} + EOF + {{- end }} + {{- end }} + + {{- if and .Values.global.secretsBackend.vault.enabled .Values.global.gossipEncryption.secretName }} + GOSSIP_KEY=`cat /vault/secrets/gossip.txt` + {{- end }} + + {{ template "consul.extraconfig" }} + + exec /usr/local/bin/docker-entrypoint.sh consul agent \ + -advertise="${ADVERTISE_IP}" \ + -config-dir=/consul/config \ + {{- if (or .Values.global.gossipEncryption.autoGenerate (and .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey)) }} + -encrypt="${GOSSIP_KEY}" \ + {{- end }} + {{- if (and .Values.global.acls.replicationToken.secretName .Values.global.acls.replicationToken.secretKey) }} + {{- if (and .Values.global.secretsBackend.vault.enabled (not .Values.global.acls.createReplicationToken)) }} + -config-file=/vault/secrets/replication-token-config.hcl \ + {{- else }} + -hcl="acl { tokens { agent = \"${ACL_REPLICATION_TOKEN}\", replication = \"${ACL_REPLICATION_TOKEN}\" } }" \ + {{- end }} + {{- end }} + {{- if and .Values.global.secretsBackend.vault.enabled .Values.global.acls.bootstrapToken.secretName }} + -config-file=/vault/secrets/bootstrap-token-config.hcl \ + {{- else if (and (not .Values.global.secretsBackend.vault.enabled) .Values.global.acls.bootstrapToken.secretName) }} + -hcl="acl { tokens { initial_management = \"${ACL_BOOTSTRAP_TOKEN}\" } }" \ + {{- end }} + {{- /* Always include the extraVolumes at the end so that users can + override other Consul settings. The last -config-dir takes + precedence. */}} + {{- range .Values.server.extraVolumes }} + {{- if .load }} + -config-dir=/consul/userconfig/{{ .name }} \ + {{- end }} + {{- end }} + -config-dir=/consul/extra-config \ + {{- if and .Values.global.cloud.enabled .Values.global.cloud.resourceId.secretName }} + -hcl="cloud { resource_id = \"${HCP_RESOURCE_ID}\" }" + {{- end }} + + {{- if .Values.global.experiments }} + {{- $commaSeparatedValues := "" }} + {{- range $index, $value := .Values.global.experiments }} + {{- if ne $index 0 }} + {{- $commaSeparatedValues = printf "%s,\\\"%s\\\"" $commaSeparatedValues $value }} + {{- else }} + {{- $commaSeparatedValues = printf "\\\"%s\\\"" $value }} + {{- end }} + {{- end }} + -hcl="experiments=[{{ $commaSeparatedValues }}]" + {{- end }} + volumeMounts: + - name: data-{{ .Release.Namespace | trunc 58 | trimSuffix "-" }} + mountPath: /consul/data + - name: config + mountPath: /consul/config + - name: extra-config + mountPath: /consul/extra-config + - name: tmp-extra-config + mountPath: /consul/tmp/extra-config + {{- if (and .Values.global.tls.enabled (not .Values.global.secretsBackend.vault.enabled)) }} + - name: consul-ca-cert + mountPath: /consul/tls/ca/ + readOnly: true + - name: consul-server-cert + mountPath: /consul/tls/server + readOnly: true + {{- end }} + {{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.enableLicenseAutoload (not .Values.global.secretsBackend.vault.enabled)) }} + - name: consul-license + mountPath: /consul/license + readOnly: true + {{- end }} + {{- if and .Values.global.metrics.datadog.enabled .Values.global.metrics.datadog.dogstatsd.enabled (eq .Values.global.metrics.datadog.dogstatsd.socketTransportType "UDS" ) }} + - name: dsdsocket + mountPath: {{ dir .Values.global.metrics.datadog.dogstatsd.dogstatsdAddr | trimAll "\"" }} + readOnly: true + {{- end }} + {{- range .Values.server.extraVolumes }} + - name: userconfig-{{ .name }} + readOnly: true + mountPath: /consul/userconfig/{{ .name }} + {{- end }} + {{- if and .Values.global.secretsBackend.vault.ca.secretName .Values.global.secretsBackend.vault.ca.secretKey }} + - name: vault-ca + mountPath: /consul/vault-ca/ + readOnly: true + {{- end }} + {{- if .Values.global.trustedCAs }} + - name: trusted-cas + mountPath: /trusted-cas + readOnly: false + {{- end }} + - name: tmp + mountPath: /tmp + readOnly: false + ports: + {{- if (or (not .Values.global.tls.enabled) (not .Values.global.tls.httpsOnly)) }} + - name: http + containerPort: 8500 + {{- end }} + {{- if .Values.global.tls.enabled }} + - name: https + containerPort: 8501 + {{- end }} + - name: grpc + containerPort: 8502 + {{- if .Values.server.exposeGossipAndRPCPorts }} + hostPort: 8502 + {{- end }} + protocol: "TCP" + - name: serflan-tcp + containerPort: {{ .Values.server.ports.serflan.port }} + {{- if .Values.server.exposeGossipAndRPCPorts }} + hostPort: {{ .Values.server.ports.serflan.port }} + {{- end }} + protocol: "TCP" + - name: serflan-udp + containerPort: {{ .Values.server.ports.serflan.port }} + {{- if .Values.server.exposeGossipAndRPCPorts }} + hostPort: {{ .Values.server.ports.serflan.port }} + {{- end }} + protocol: "UDP" + - name: serfwan-tcp + containerPort: 8302 + {{- if .Values.server.exposeGossipAndRPCPorts }} + hostPort: 8302 + {{- end }} + protocol: "TCP" + - name: serfwan-udp + containerPort: 8302 + {{- if .Values.server.exposeGossipAndRPCPorts }} + hostPort: 8302 + {{- end }} + protocol: "UDP" + - name: server + containerPort: 8300 + {{- if .Values.server.exposeGossipAndRPCPorts }} + hostPort: 8300 + {{- end }} + - name: dns-tcp + containerPort: 8600 + protocol: "TCP" + - name: dns-udp + containerPort: 8600 + protocol: "UDP" + readinessProbe: + # NOTE(mitchellh): when our HTTP status endpoints support the + # proper status codes, we should switch to that. This is temporary. + exec: + command: + - "/bin/sh" + - "-ec" + - | + {{- if .Values.global.tls.enabled }} + curl -k \ + https://127.0.0.1:8501/v1/status/leader \ + {{- else }} + curl http://127.0.0.1:8500/v1/status/leader \ + {{- end }} + 2>/dev/null | grep -E '".+"' + failureThreshold: 2 + initialDelaySeconds: 5 + periodSeconds: 3 + successThreshold: 1 + timeoutSeconds: 5 + {{- if .Values.server.resources }} + resources: + {{- if eq (typeOf .Values.server.resources) "string" }} + {{ tpl .Values.server.resources . | nindent 12 | trim }} + {{- else }} + {{- toYaml .Values.server.resources | nindent 12 }} + {{- end }} + {{- end }} + {{- if .Values.server.containerSecurityContext.server }} + securityContext: + {{- toYaml .Values.server.containerSecurityContext.server | nindent 12 }} + {{- else }} + {{- include "consul.restrictedSecurityContext" . | nindent 10 }} + {{- end }} + {{- if .Values.server.extraContainers }} + {{ toYaml .Values.server.extraContainers | nindent 8 }} + {{- end }} + {{- if .Values.server.snapshotAgent.enabled }} + - name: consul-snapshot-agent + image: "{{ default .Values.global.image .Values.server.image }}" + {{ template "consul.imagePullPolicy" . }} + env: + {{- if .Values.server.snapshotAgent.caCert }} + - name: SSL_CERT_DIR + value: "/etc/ssl/certs:/extra-ssl-certs" + {{- end }} + {{- if .Values.global.tls.enabled }} + - name: CONSUL_HTTP_ADDR + value: https://127.0.0.1:8501 + - name: CONSUL_CACERT + {{- if .Values.global.secretsBackend.vault.enabled }} + value: /vault/secrets/serverca.crt + {{- else }} + value: /consul/tls/ca/tls.crt + {{- end }} + {{- else }} + - name: CONSUL_HTTP_ADDR + value: http://127.0.0.1:8500 + {{- end }} + {{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey .Values.global.enterpriseLicense.enableLicenseAutoload (not .Values.global.acls.manageSystemACLs)) }} + - name: CONSUL_LICENSE_PATH + {{- if .Values.global.secretsBackend.vault.enabled }} + value: /vault/secrets/enterpriselicense.txt + {{- else }} + value: /consul/license/{{ .Values.global.enterpriseLicense.secretKey }} + {{- end }} + {{- end }} + command: + - "/bin/sh" + - "-ec" + - | + {{- if .Values.server.snapshotAgent.caCert }} + cat < /extra-ssl-certs/custom-ca.pem + {{- .Values.server.snapshotAgent.caCert | nindent 14 }} + EOF + {{- end }} + exec /bin/consul snapshot agent \ + -interval={{ .Values.server.snapshotAgent.interval }} \ + {{- if .Values.global.acls.manageSystemACLs }} + -config-file=/consul/config/snapshot-login.json \ + {{- end }} + {{- if (and .Values.server.snapshotAgent.configSecret.secretName .Values.server.snapshotAgent.configSecret.secretKey) }} + {{- if .Values.global.secretsBackend.vault.enabled }} + -config-file=/vault/secrets/snapshot-agent-config.json \ + {{- else }} + -config-dir=/consul/user-config \ + {{- end }} + {{- end }} + volumeMounts: + {{- if .Values.global.acls.manageSystemACLs }} + - name: snapshot-agent-config + mountPath: /consul/config + readOnly: true + {{- end }} + {{- if .Values.server.snapshotAgent.caCert }} + - name: extra-ssl-certs + mountPath: /extra-ssl-certs + readOnly: false + {{- end }} + {{- if (and .Values.server.snapshotAgent.configSecret.secretName .Values.server.snapshotAgent.configSecret.secretKey (not .Values.global.secretsBackend.vault.enabled)) }} + - name: snapshot-agent-user-config + mountPath: /consul/user-config + readOnly: true + {{- end }} + {{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey .Values.global.enterpriseLicense.enableLicenseAutoload (not .Values.global.secretsBackend.vault.enabled) (not .Values.global.acls.manageSystemACLs))}} + - name: consul-license + mountPath: /consul/license + readOnly: true + {{- end }} + {{- if and .Values.global.tls.enabled (not .Values.global.secretsBackend.vault.enabled) }} + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- with .Values.server.snapshotAgent.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- end }} + {{- if .Values.server.nodeSelector }} + nodeSelector: + {{ tpl .Values.server.nodeSelector . | indent 8 | trim }} + {{- end }} + volumeClaimTemplates: + - metadata: + name: data-{{ .Release.Namespace | trunc 58 | trimSuffix "-" }} + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .Values.server.storage }} + {{- if .Values.server.storageClass }} + storageClassName: {{ .Values.server.storageClass }} + {{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/server-tmp-extra-config-configmap.yaml b/charts/hashicorp/consul/1.5.3/templates/server-tmp-extra-config-configmap.yaml new file mode 100644 index 0000000000..a42d6d09f6 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/server-tmp-extra-config-configmap.yaml @@ -0,0 +1,21 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +# ConfigMap that is used as a temporary landing spot so that the container command +# in the server-stateful set where it needs to be transformed. ConfigMaps create +# read only volumes so it needs to be copied and transformed to the extra-config +# emptyDir volume where all final extra cofngi lives for use in consul. (locality-init +# also writes to extra-config volume.) +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "consul.fullname" . }}-server-tmp-extra-config + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server +data: + extra-from-values.json: |- +{{ tpl .Values.server.extraConfig . | trimAll "\"" | indent 4 }} +{{- end }} \ No newline at end of file diff --git a/charts/hashicorp/consul/1.5.3/templates/sync-catalog-clusterrole.yaml b/charts/hashicorp/consul/1.5.3/templates/sync-catalog-clusterrole.yaml new file mode 100644 index 0000000000..89ea9f3c5c --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/sync-catalog-clusterrole.yaml @@ -0,0 +1,60 @@ +{{- $syncEnabled := (or (and (ne (.Values.syncCatalog.enabled | toString) "-") .Values.syncCatalog.enabled) (and (eq (.Values.syncCatalog.enabled | toString) "-") .Values.global.enabled)) }} +{{- if $syncEnabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "consul.fullname" . }}-sync-catalog + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: sync-catalog +rules: +- apiGroups: [ "" ] + resources: + - services + verbs: + - get + - list + - watch +{{- if .Values.syncCatalog.toK8S }} + - update + - patch + - delete + - create +{{- end }} +- apiGroups: ["discovery.k8s.io"] + resources: + - endpointslices + verbs: + - get + - list + - watch +{{- if .Values.syncCatalog.toK8S }} + - update + - patch + - delete + - create +{{- end }} +- apiGroups: [ "" ] + resources: + - nodes + verbs: + - get +{{- if .Values.global.enablePodSecurityPolicies }} +- apiGroups: [ "policy" ] + resources: [ "podsecuritypolicies" ] + verbs: + - use + resourceNames: + - {{ template "consul.fullname" . }}-sync-catalog +{{- end }} +- apiGroups: [ "networking.k8s.io" ] + resources: + - ingresses + verbs: + - get + - list + - watch +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/sync-catalog-clusterrolebinding.yaml b/charts/hashicorp/consul/1.5.3/templates/sync-catalog-clusterrolebinding.yaml new file mode 100644 index 0000000000..818823cca3 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/sync-catalog-clusterrolebinding.yaml @@ -0,0 +1,21 @@ +{{- $syncEnabled := (or (and (ne (.Values.syncCatalog.enabled | toString) "-") .Values.syncCatalog.enabled) (and (eq (.Values.syncCatalog.enabled | toString) "-") .Values.global.enabled)) }} +{{- if $syncEnabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "consul.fullname" . }}-sync-catalog + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: sync-catalog +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "consul.fullname" . }}-sync-catalog +subjects: + - kind: ServiceAccount + name: {{ template "consul.fullname" . }}-sync-catalog + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/sync-catalog-deployment.yaml b/charts/hashicorp/consul/1.5.3/templates/sync-catalog-deployment.yaml new file mode 100644 index 0000000000..963e6b2485 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/sync-catalog-deployment.yaml @@ -0,0 +1,238 @@ +{{- if (or (and (ne (.Values.syncCatalog.enabled | toString) "-") .Values.syncCatalog.enabled) (and (eq (.Values.syncCatalog.enabled | toString) "-") .Values.global.enabled)) }} +{{- template "consul.reservedNamesFailer" (list .Values.syncCatalog.consulNamespaces.consulDestinationNamespace "syncCatalog.consulNamespaces.consulDestinationNamespace") }} +{{ template "consul.validateRequiredCloudSecretsExist" . }} +{{ template "consul.validateCloudSecretKeys" . }} +# The deployment for running the sync-catalog pod +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "consul.fullname" . }}-sync-catalog + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: sync-catalog + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} +spec: + replicas: 1 + selector: + matchLabels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: sync-catalog + template: + metadata: + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: sync-catalog + {{- if .Values.syncCatalog.extraLabels }} + {{- toYaml .Values.syncCatalog.extraLabels | nindent 8 }} + {{- end }} + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} + annotations: + "consul.hashicorp.com/connect-inject": "false" + "consul.hashicorp.com/mesh-inject": "false" + {{- if .Values.syncCatalog.annotations }} + {{- tpl .Values.syncCatalog.annotations . | nindent 8 }} + {{- end }} + {{- if (and .Values.global.secretsBackend.vault.enabled .Values.global.tls.enabled) }} + "vault.hashicorp.com/agent-init-first": "true" + "vault.hashicorp.com/agent-inject": "true" + "vault.hashicorp.com/role": {{ .Values.global.secretsBackend.vault.consulCARole }} + "vault.hashicorp.com/agent-inject-secret-serverca.crt": {{ .Values.global.tls.caCert.secretName }} + "vault.hashicorp.com/agent-inject-template-serverca.crt": {{ template "consul.serverTLSCATemplate" . }} + {{- if and .Values.global.secretsBackend.vault.ca.secretName .Values.global.secretsBackend.vault.ca.secretKey }} + "vault.hashicorp.com/agent-extra-secret": "{{ .Values.global.secretsBackend.vault.ca.secretName }}" + "vault.hashicorp.com/ca-cert": "/vault/custom/{{ .Values.global.secretsBackend.vault.ca.secretKey }}" + {{- end }} + {{- if .Values.global.secretsBackend.vault.agentAnnotations }} + {{ tpl .Values.global.secretsBackend.vault.agentAnnotations . | nindent 8 | trim }} + {{- end }} + {{- if (and (.Values.global.secretsBackend.vault.vaultNamespace) (not (hasKey (default "" .Values.global.secretsBackend.vault.agentAnnotations | fromYaml) "vault.hashicorp.com/namespace")))}} + "vault.hashicorp.com/namespace": "{{ .Values.global.secretsBackend.vault.vaultNamespace }}" + {{- end }} + {{- end }} + spec: + serviceAccountName: {{ template "consul.fullname" . }}-sync-catalog + volumes: + {{- if .Values.global.tls.enabled }} + {{- if not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled) }} + - name: consul-ca-cert + secret: + {{- if .Values.global.tls.caCert.secretName }} + secretName: {{ .Values.global.tls.caCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" . }}-ca-cert + {{- end }} + items: + - key: {{ default "tls.crt" .Values.global.tls.caCert.secretKey }} + path: tls.crt + {{- end }} + {{- end }} + containers: + - name: sync-catalog + image: "{{ default .Values.global.imageK8S .Values.syncCatalog.image }}" + {{ template "consul.imagePullPolicy" . }} + {{- include "consul.restrictedSecurityContext" . | nindent 8 }} + env: + {{- include "consul.consulK8sConsulServerEnvVars" . | nindent 8 }} + {{- if .Values.global.acls.manageSystemACLs }} + - name: CONSUL_LOGIN_AUTH_METHOD + {{- if and .Values.global.federation.enabled .Values.global.federation.primaryDatacenter .Values.global.enableConsulNamespaces }} + value: {{ template "consul.fullname" . }}-k8s-component-auth-method-{{ .Values.global.datacenter }} + {{- else }} + value: {{ template "consul.fullname" . }}-k8s-component-auth-method + {{- end }} + - name: CONSUL_LOGIN_DATACENTER + {{- if and .Values.global.federation.enabled .Values.global.federation.primaryDatacenter .Values.global.enableConsulNamespaces }} + value: {{ .Values.global.federation.primaryDatacenter }} + {{- else }} + value: {{ .Values.global.datacenter }} + {{- end }} + - name: CONSUL_LOGIN_META + value: "component=sync-catalog,pod=$(NAMESPACE)/$(POD_NAME)" + {{- end }} + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + {{- if (and .Values.syncCatalog.aclSyncToken.secretName .Values.syncCatalog.aclSyncToken.secretKey) }} + - name: CONSUL_ACL_TOKEN + valueFrom: + secretKeyRef: + name: {{ .Values.syncCatalog.aclSyncToken.secretName }} + key: {{ .Values.syncCatalog.aclSyncToken.secretKey }} + {{- end }} + volumeMounts: + {{- if .Values.global.tls.enabled }} + {{- if not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled) }} + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- end }} + command: + - "/bin/sh" + - "-ec" + - | + exec consul-k8s-control-plane sync-catalog \ + -log-level={{ default .Values.global.logLevel .Values.syncCatalog.logLevel }} \ + -log-json={{ .Values.global.logJSON }} \ + -k8s-default-sync={{ .Values.syncCatalog.default }} \ + {{- if (not .Values.syncCatalog.toConsul) }} + -to-consul=false \ + {{- end }} + {{- if (not .Values.syncCatalog.toK8S) }} + -to-k8s=false \ + {{- end }} + -consul-domain={{ .Values.global.domain }} \ + {{- if .Values.syncCatalog.k8sPrefix }} + -k8s-service-prefix="{{ .Values.syncCatalog.k8sPrefix}}" \ + {{- end }} + {{- if .Values.syncCatalog.k8sSourceNamespace }} + -k8s-source-namespace="{{ .Values.syncCatalog.k8sSourceNamespace}}" \ + {{- end }} + {{- range $value := .Values.syncCatalog.k8sAllowNamespaces }} + -allow-k8s-namespace="{{ $value }}" \ + {{- end }} + {{- range $value := .Values.syncCatalog.k8sDenyNamespaces }} + -deny-k8s-namespace="{{ $value }}" \ + {{- end }} + -k8s-write-namespace=${NAMESPACE} \ + {{- if (not .Values.syncCatalog.syncClusterIPServices) }} + -sync-clusterip-services=false \ + {{- end }} + {{- if .Values.syncCatalog.nodePortSyncType }} + -node-port-sync-type={{ .Values.syncCatalog.nodePortSyncType }} \ + {{- end }} + {{- if .Values.syncCatalog.consulWriteInterval }} + -consul-write-interval={{ .Values.syncCatalog.consulWriteInterval }} \ + {{- end }} + {{- if .Values.syncCatalog.k8sTag }} + -consul-k8s-tag={{ .Values.syncCatalog.k8sTag }} \ + {{- end }} + {{- if .Values.syncCatalog.consulNodeName }} + -consul-node-name={{ .Values.syncCatalog.consulNodeName }} \ + {{- end }} + {{- if .Values.global.adminPartitions.enabled }} + -partition={{ .Values.global.adminPartitions.name }} \ + {{- end }} + {{- if .Values.syncCatalog.consulPrefix}} + -consul-service-prefix="{{ .Values.syncCatalog.consulPrefix}}" \ + {{- end}} + {{- if .Values.syncCatalog.addK8SNamespaceSuffix}} + -add-k8s-namespace-suffix \ + {{- end}} + {{- if .Values.global.enableConsulNamespaces }} + -enable-namespaces=true \ + {{- if .Values.syncCatalog.consulNamespaces.consulDestinationNamespace }} + -consul-destination-namespace={{ .Values.syncCatalog.consulNamespaces.consulDestinationNamespace }} \ + {{- end }} + {{- if .Values.syncCatalog.consulNamespaces.mirroringK8S }} + -enable-k8s-namespace-mirroring=true \ + {{- if .Values.syncCatalog.consulNamespaces.mirroringK8SPrefix }} + -k8s-namespace-mirroring-prefix={{ .Values.syncCatalog.consulNamespaces.mirroringK8SPrefix }} \ + {{- end }} + {{- end }} + {{- if .Values.global.acls.manageSystemACLs }} + -consul-cross-namespace-acl-policy=cross-namespace-policy \ + {{- end }} + {{- end }} + {{- if .Values.syncCatalog.ingress.enabled }} + -enable-ingress=true \ + {{- if .Values.syncCatalog.ingress.loadBalancerIPs }} + -loadBalancer-ips=true \ + {{- end }} + {{- end }} + {{- if .Values.syncCatalog.syncLoadBalancerEndpoints }} + -sync-lb-services-endpoints=true \ + {{- end }} + livenessProbe: + httpGet: + path: /health/ready + port: 8080 + scheme: HTTP + failureThreshold: 3 + initialDelaySeconds: 30 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 5 + readinessProbe: + httpGet: + path: /health/ready + port: 8080 + scheme: HTTP + failureThreshold: 5 + initialDelaySeconds: 10 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 5 + {{- with .Values.syncCatalog.resources }} + resources: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- if .Values.syncCatalog.priorityClassName }} + priorityClassName: {{ .Values.syncCatalog.priorityClassName | quote }} + {{- end }} + {{- if .Values.syncCatalog.nodeSelector }} + nodeSelector: + {{ tpl .Values.syncCatalog.nodeSelector . | indent 8 | trim }} + {{- end }} + {{- if .Values.syncCatalog.affinity }} + affinity: + {{ tpl .Values.syncCatalog.affinity . | indent 8 | trim }} + {{- end }} + {{- if .Values.syncCatalog.tolerations }} + tolerations: + {{ tpl .Values.syncCatalog.tolerations . | indent 8 | trim }} + {{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/sync-catalog-podsecuritypolicy.yaml b/charts/hashicorp/consul/1.5.3/templates/sync-catalog-podsecuritypolicy.yaml new file mode 100644 index 0000000000..cc70feaab1 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/sync-catalog-podsecuritypolicy.yaml @@ -0,0 +1,40 @@ +{{- if (and .Values.global.enablePodSecurityPolicies (or (and (ne (.Values.syncCatalog.enabled | toString) "-") .Values.syncCatalog.enabled) (and (eq (.Values.syncCatalog.enabled | toString) "-") .Values.global.enabled))) }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-sync-catalog + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: sync-catalog +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + # Allow core volume types. + volumes: + - 'configMap' + - 'emptyDir' + - 'projected' + - 'secret' + - 'downwardAPI' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/sync-catalog-serviceaccount.yaml b/charts/hashicorp/consul/1.5.3/templates/sync-catalog-serviceaccount.yaml new file mode 100644 index 0000000000..deab1ad075 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/sync-catalog-serviceaccount.yaml @@ -0,0 +1,24 @@ +{{- $syncEnabled := (or (and (ne (.Values.syncCatalog.enabled | toString) "-") .Values.syncCatalog.enabled) (and (eq (.Values.syncCatalog.enabled | toString) "-") .Values.global.enabled)) }} +{{- if $syncEnabled }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-sync-catalog + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: sync-catalog + {{- if .Values.syncCatalog.serviceAccount.annotations }} + annotations: + {{ tpl .Values.syncCatalog.serviceAccount.annotations . | nindent 4 | trim }} + {{- end }} +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/telemetry-collector-configmap.yaml b/charts/hashicorp/consul/1.5.3/templates/telemetry-collector-configmap.yaml new file mode 100644 index 0000000000..0bf5b8753c --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/telemetry-collector-configmap.yaml @@ -0,0 +1,18 @@ +{{- if (and .Values.telemetryCollector.enabled .Values.telemetryCollector.customExporterConfig) }} +# Immutable ConfigMap which saves the partition name. Attempting to update this configmap +# with a new Admin Partition name will cause the helm upgrade to fail +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "consul.fullname" . }}-telemetry-collector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: consul-telemetry-collector +data: + config.json: |- + {{ tpl .Values.telemetryCollector.customExporterConfig . | trimAll "\"" | indent 4 }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/telemetry-collector-deployment.yaml b/charts/hashicorp/consul/1.5.3/templates/telemetry-collector-deployment.yaml new file mode 100644 index 0000000000..78326998f8 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/telemetry-collector-deployment.yaml @@ -0,0 +1,453 @@ +{{- if and .Values.telemetryCollector.enabled (not (mustHas "resource-apis" .Values.global.experiments)) }} +{{- if not .Values.telemetryCollector.image}}{{ fail "telemetryCollector.image must be set to enable consul-telemetry-collector" }}{{ end }} +{{- if not .Values.connectInject.enabled }}{{ fail "connectInject.enabled must be true" }}{{ end -}} +{{- if and .Values.global.adminPartitions.enabled (not .Values.global.enableConsulNamespaces) }}{{ fail "global.enableConsulNamespaces must be true if global.adminPartitions.enabled=true" }}{{ end }} +{{ template "consul.validateCloudSecretKeys" . }} +{{ template "consul.validateTelemetryCollectorCloud" . }} +{{ template "consul.validateTelemetryCollectorCloudSecretKeys" . }} +{{ template "consul.validateTelemetryCollectorResourceId" . }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "consul.fullname" . }}-telemetry-collector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: consul-telemetry-collector + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} +spec: + replicas: {{ .Values.telemetryCollector.replicas }} + selector: + matchLabels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: consul-telemetry-collector + template: + metadata: + annotations: + "consul.hashicorp.com/connect-inject": "false" + # This annotation tells the endpoints controller that this pod was injected even though it wasn't. The + # endpoints controller would then sync the endpoint into Consul + "consul.hashicorp.com/connect-inject-status": "injected" + # Signals to the endpoints controller that we should force Consul NS creation, since we bypass the mesh webhook. + "consul.hashicorp.com/telemetry-collector": "true" + # We aren't using tproxy and we don't have an original pod. This would be simpler if we made a path similar + # to gateways + "consul.hashicorp.com/connect-service-port": "metricsserver" + "consul.hashicorp.com/transparent-proxy": "false" + "consul.hashicorp.com/transparent-proxy-overwrite-probes": "false" + "consul.hashicorp.com/connect-k8s-version": {{ $.Chart.Version }} + {{- if .Values.telemetryCollector.customExporterConfig }} + # configmap checksum + "consul.hashicorp.com/config-checksum": {{ include (print $.Template.BasePath "/telemetry-collector-configmap.yaml") . | sha256sum }} + {{- end }} + # vault annotations + {{- if (and .Values.global.secretsBackend.vault.enabled .Values.global.tls.enabled) }} + "vault.hashicorp.com/agent-init-first": "true" + "vault.hashicorp.com/agent-inject": "true" + "vault.hashicorp.com/role": {{ .Values.global.secretsBackend.vault.consulCARole }} + "vault.hashicorp.com/agent-inject-secret-serverca.crt": {{ .Values.global.tls.caCert.secretName }} + "vault.hashicorp.com/agent-inject-template-serverca.crt": {{ template "consul.serverTLSCATemplate" . }} + {{- if and .Values.global.secretsBackend.vault.ca.secretName .Values.global.secretsBackend.vault.ca.secretKey }} + "vault.hashicorp.com/agent-extra-secret": "{{ .Values.global.secretsBackend.vault.ca.secretName }}" + "vault.hashicorp.com/ca-cert": "/vault/custom/{{ .Values.global.secretsBackend.vault.ca.secretKey }}" + {{- end }} + {{- if .Values.global.secretsBackend.vault.agentAnnotations }} + {{ tpl .Values.global.secretsBackend.vault.agentAnnotations . | nindent 8 | trim }} + {{- end }} + {{- if (and (.Values.global.secretsBackend.vault.vaultNamespace) (not (hasKey (default "" .Values.global.secretsBackend.vault.agentAnnotations | fromYaml) "vault.hashicorp.com/namespace")))}} + "vault.hashicorp.com/namespace": "{{ .Values.global.secretsBackend.vault.vaultNamespace }}" + {{- end }} + {{- end }} + + labels: + consul.hashicorp.com/connect-inject-managed-by: consul-k8s-endpoints-controller + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: consul-telemetry-collector + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} + spec: + # This needs to explicitly be consul-telemetry-collector because we look this up from each service consul-dataplane + # to forward metrics to it. + serviceAccountName: consul-telemetry-collector + initContainers: + # We're manually managing this init container instead of using the connect injector so that we don't run into + # any race conditions on the connect-injector deployment or upgrade + - name: consul-connect-init + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: CONSUL_NODE_NAME + value: $(NODE_NAME)-virtual + {{- include "consul.consulK8sConsulServerEnvVars" . | nindent 10 }} + # acl login info + {{- if .Values.global.acls.manageSystemACLs }} + - name: CONSUL_LOGIN_AUTH_METHOD + value: {{ template "consul.fullname" . }}-k8s-auth-method + - name: CONSUL_LOGIN_DATACENTER + value: {{ .Values.global.datacenter }} + - name: CONSUL_LOGIN_META + value: "component=consul-telemetry-collector,pod=$(NAMESPACE)/$(POD_NAME)" + {{- end }} + # service and login namespace + # this is attempting to replicate the behavior of webhooks in calculating namespace + # https://github.com/hashicorp/consul-k8s/blob/b84339050bb2c4b62b60cec96275f74952b0ac9d/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go#L200 + {{- if .Values.global.enableConsulNamespaces }} + {{- if .Values.connectInject.consulNamespaces.mirroringK8S }} + - name: CONSUL_NAMESPACE + value: {{ .Values.connectInject.consulNamespaces.mirroringK8SPrefix }}{{ .Release.Namespace }} + {{- else }} + - name: CONSUL_NAMESPACE + value: {{ .Values.connectInject.consulNamespaces.consulDestinationNamespace }} + {{- end }} + {{- if .Values.global.acls.manageSystemACLs }} + {{- if .Values.connectInject.consulNamespaces.mirroringK8S }} + - name: CONSUL_LOGIN_NAMESPACE + value: default + {{- else }} + - name: CONSUL_LOGIN_NAMESPACE + value: {{ .Values.connectInject.consulNamespaces.consulDestinationNamespace }} + {{- end }} + {{- end }} + {{- end }} + command: + - /bin/sh + - -ec + - |- + exec consul-k8s-control-plane connect-init \ + -log-json={{ .Values.global.logJSON }} \ + -log-level={{ default .Values.global.logLevel .Values.telemetryCollector.logLevel }} \ + -pod-name=${POD_NAME} \ + -pod-namespace=${POD_NAMESPACE} \ + -proxy-id-file="/consul/connect-inject/proxyid" \ + -service-account-name="consul-telemetry-collector" \ + -service-name="" + + image: {{ .Values.global.imageK8S }} + {{ template "consul.imagePullPolicy" . }} + {{- if .Values.telemetryCollector.initContainer.resources }} + resources: + {{- toYaml .Values.telemetryCollector.initContainer.resources | nindent 12 }} + {{- else }} + resources: + limits: + cpu: 50m + memory: 150Mi + requests: + cpu: 50m + memory: 25Mi + {{- end }} + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /consul/connect-inject + name: consul-connect-inject-data + {{- if .Values.global.tls.enabled }} + {{- if not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled) }} + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- end }} + containers: + - name: consul-telemetry-collector + image: {{ .Values.telemetryCollector.image }} + {{ template "consul.imagePullPolicy" . }} + ports: + - containerPort: 9090 + name: metrics + protocol: TCP + - containerPort: 9356 + name: metricsserver + protocol: TCP + env: + # These are mounted as secrets so that the telemetry-collector can use them when cloud is enabled. + # - the hcp-go-sdk in consul agent will already look for HCP_CLIENT_ID, HCP_CLIENT_SECRET, HCP_AUTH_URL, + # HCP_SCADA_ADDRESS, and HCP_API_HOST. so nothing more needs to be done. + # - HCP_RESOURCE_ID is created either in the global cloud section or in telemetryCollector.cloud + {{- if .Values.telemetryCollector.cloud.resourceId.secretName }} + - name: HCP_RESOURCE_ID + valueFrom: + secretKeyRef: + name: {{ .Values.telemetryCollector.cloud.resourceId.secretName }} + key: {{ .Values.telemetryCollector.cloud.resourceId.secretKey }} + {{- else if .Values.global.cloud.resourceId.secretName }} + - name: HCP_RESOURCE_ID + valueFrom: + secretKeyRef: + name: {{ .Values.global.cloud.resourceId.secretName }} + key: {{ .Values.global.cloud.resourceId.secretKey }} + {{- end }} + {{- if .Values.telemetryCollector.cloud.clientId.secretName }} + - name: HCP_CLIENT_ID + valueFrom: + secretKeyRef: + name: {{ .Values.telemetryCollector.cloud.clientId.secretName }} + key: {{ .Values.telemetryCollector.cloud.clientId.secretKey }} + {{- else if .Values.global.cloud.clientId.secretName }} + - name: HCP_CLIENT_ID + valueFrom: + secretKeyRef: + name: {{ .Values.global.cloud.clientId.secretName }} + key: {{ .Values.global.cloud.clientId.secretKey }} + {{- end }} + {{- if .Values.telemetryCollector.cloud.clientSecret.secretName }} + - name: HCP_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: {{ .Values.telemetryCollector.cloud.clientSecret.secretName }} + key: {{ .Values.telemetryCollector.cloud.clientSecret.secretKey }} + {{- else if .Values.global.cloud.clientSecret.secretName }} + - name: HCP_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: {{ .Values.global.cloud.clientSecret.secretName }} + key: {{ .Values.global.cloud.clientSecret.secretKey }} + {{- end}} + {{- if .Values.global.cloud.authUrl.secretName }} + - name: HCP_AUTH_URL + valueFrom: + secretKeyRef: + name: {{ .Values.global.cloud.authUrl.secretName }} + key: {{ .Values.global.cloud.authUrl.secretKey }} + {{- end}} + {{- if .Values.global.cloud.apiHost.secretName }} + - name: HCP_API_HOST + valueFrom: + secretKeyRef: + name: {{ .Values.global.cloud.apiHost.secretName }} + key: {{ .Values.global.cloud.apiHost.secretKey }} + {{- end}} + {{- if .Values.global.cloud.scadaAddress.secretName }} + - name: HCP_SCADA_ADDRESS + valueFrom: + secretKeyRef: + name: {{ .Values.global.cloud.scadaAddress.secretName }} + key: {{ .Values.global.cloud.scadaAddress.secretKey }} + {{- end}} + {{- if .Values.global.trustedCAs }} + - name: SSL_CERT_DIR + value: "/etc/ssl/certs:/trusted-cas" + {{- end }} + {{- if .Values.global.metrics.datadog.otlp.enabled }} + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + {{- if eq (.Values.global.metrics.datadog.otlp.protocol | lower ) "http" }} + - name: CO_OTEL_HTTP_ENDPOINT + value: "http://$(HOST_IP):4318" + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: "http://$(HOST_IP):4318" + {{- else if eq (.Values.global.metrics.datadog.otlp.protocol | lower) "grpc" }} + - name: CO_OTEL_HTTP_ENDPOINT + value: "http://$(HOST_IP):4317" + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: "http://$(HOST_IP):4317" + {{- end }} + {{- end }} + {{- include "consul.extraEnvironmentVars" .Values.telemetryCollector | nindent 12 }} + command: + - "/bin/sh" + - "-ec" + - | + {{- if .Values.global.trustedCAs }} + {{- range $i, $cert := .Values.global.trustedCAs }} + cat < /trusted-cas/custom-ca-{{$i}}.pem + {{- $cert | nindent 10 }} + EOF + {{- end }} + {{- end }} + + exec consul-telemetry-collector agent \ + {{- if .Values.telemetryCollector.customExporterConfig }} + -config-file-path /consul/config/config.json \ + {{ end }} + volumeMounts: + {{- if .Values.telemetryCollector.customExporterConfig }} + - name: config + mountPath: /consul/config + {{- end }} + {{- if .Values.global.trustedCAs }} + - name: trusted-cas + mountPath: /trusted-cas + readOnly: false + {{- end }} + resources: + {{- if .Values.telemetryCollector.resources }} + {{- toYaml .Values.telemetryCollector.resources | nindent 12 }} + {{- end }} + # consul-dataplane container + - name: consul-dataplane + image: "{{ .Values.global.imageConsulDataplane }}" + {{ template "consul.imagePullPolicy" . }} + command: + - consul-dataplane + args: + # addresses + {{- if .Values.externalServers.enabled }} + - -addresses={{ .Values.externalServers.hosts | first }} + {{- else }} + - -addresses={{ template "consul.fullname" . }}-server.{{ .Release.Namespace }}.svc + {{- end }} + # grpc + {{- if .Values.externalServers.enabled }} + - -grpc-port={{ .Values.externalServers.grpcPort }} + {{- else }} + - -grpc-port=8502 + {{- end }} + - -proxy-service-id-path=/consul/connect-inject/proxyid + # tls + {{- if .Values.global.tls.enabled }} + {{- if (not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots)) }} + {{- if .Values.global.secretsBackend.vault.enabled }} + - -ca-certs=/vault/secrets/serverca.crt + {{- else }} + - -ca-certs=/consul/tls/ca/tls.crt + {{- end }} + {{- end }} + {{- if and .Values.externalServers.enabled .Values.externalServers.tlsServerName }} + - -tls-server-name={{.Values.externalServers.tlsServerName }} + {{- else if .Values.global.cloud.enabled }} + - -tls-server-name=server.{{ .Values.global.datacenter}}.{{ .Values.global.domain}} + {{- end }} + {{- else }} + - -tls-disabled + {{- end }} + # credentials + {{- if .Values.global.acls.manageSystemACLs }} + - -credential-type=login + - -login-bearer-token-path=/var/run/secrets/kubernetes.io/serviceaccount/token + - -login-auth-method={{ template "consul.fullname" . }}-k8s-auth-method + {{- end }} + # service and login namespace + {{- if .Values.global.enableConsulNamespaces }} + {{- if .Values.connectInject.consulNamespaces.mirroringK8S }} + - -service-namespace={{ .Values.connectInject.consulNamespaces.mirroringK8SPrefix }}{{ .Release.Namespace }} + {{- else }} + - -service-namespace={{ .Values.connectInject.consulNamespaces.consulDestinationNamespace }} + {{- end }} + {{- if .Values.global.acls.manageSystemACLs }} + {{- if .Values.connectInject.consulNamespaces.mirroringK8S }} + - -login-namespace=default + {{- else }} + - -login-namespace={{ .Values.connectInject.consulNamespaces.consulDestinationNamespace }} + {{- end }} + {{- end }} + {{- end }} + # service and login partition + {{- if .Values.global.adminPartitions.enabled }} + - -service-partition={{ .Values.global.adminPartitions.name }} + {{- if .Values.global.acls.manageSystemACLs }} + - -login-partition={{ .Values.global.adminPartitions.name }} + {{- end }} + {{- end }} + # telemetry + {{- if .Values.global.metrics.enabled }} + - -telemetry-prom-scrape-path=/metrics + {{- end }} + - -log-level={{ default .Values.global.logLevel .Values.telemetryCollector.logLevel }} + - -log-json={{ .Values.global.logJSON }} + - -envoy-concurrency=2 + {{- if and .Values.externalServers.enabled .Values.externalServers.skipServerWatch }} + - -server-watch-disabled=true + {{- end }} + env: + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: DP_CREDENTIAL_LOGIN_META1 + value: pod=$(NAMESPACE)/$(POD_NAME) + - name: DP_CREDENTIAL_LOGIN_META2 + value: component=consul-telemetry-collector + - name: DP_SERVICE_NODE_NAME + value: $(NODE_NAME)-virtual + - name: TMPDIR + value: /consul/connect-inject + readinessProbe: + failureThreshold: 3 + initialDelaySeconds: 1 + periodSeconds: 10 + successThreshold: 1 + tcpSocket: + port: 20000 + timeoutSeconds: 1 + securityContext: + readOnlyRootFilesystem: true + runAsGroup: 5995 + runAsNonRoot: true + runAsUser: 5995 + # dataplane volume mounts + volumeMounts: + - mountPath: /consul/connect-inject + name: consul-connect-inject-data + {{- if .Values.global.tls.enabled }} + {{- if not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled) }} + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- end }} + + {{- if .Values.telemetryCollector.nodeSelector }} + nodeSelector: + {{ tpl .Values.telemetryCollector.nodeSelector . | indent 8 | trim }} + {{- end }} + {{- if .Values.telemetryCollector.priorityClassName }} + priorityClassName: {{ .Values.telemetryCollector.priorityClassName }} + {{- end }} + volumes: + - emptyDir: + medium: Memory + name: consul-connect-inject-data + {{- if .Values.global.trustedCAs }} + - name: trusted-cas + emptyDir: + medium: "Memory" + {{- end }} + {{- if .Values.global.tls.enabled }} + {{- if not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled) }} + - name: consul-ca-cert + secret: + {{- if .Values.global.tls.caCert.secretName }} + secretName: {{ .Values.global.tls.caCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" . }}-ca-cert + {{- end }} + items: + - key: {{ default "tls.crt" .Values.global.tls.caCert.secretKey }} + path: tls.crt + {{- end }} + {{- end }} + - name: config + configMap: + name: {{ template "consul.fullname" . }}-telemetry-collector +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/telemetry-collector-podsecuritypolicy.yaml b/charts/hashicorp/consul/1.5.3/templates/telemetry-collector-podsecuritypolicy.yaml new file mode 100644 index 0000000000..f4c05a2f33 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/telemetry-collector-podsecuritypolicy.yaml @@ -0,0 +1,42 @@ +{{- if and .Values.global.enablePodSecurityPolicies .Values.telemetryCollector.enabled }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-telemetry-collector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: telemetry-collector +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + defaultAddCapabilities: + - NET_BIND_SERVICE + # Allow core volume types. + volumes: + - 'configMap' + - 'emptyDir' + - 'projected' + - 'secret' + - 'downwardAPI' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/telemetry-collector-role.yaml b/charts/hashicorp/consul/1.5.3/templates/telemetry-collector-role.yaml new file mode 100644 index 0000000000..f89373252d --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/telemetry-collector-role.yaml @@ -0,0 +1,21 @@ +{{- if and .Values.global.enablePodSecurityPolicies .Values.telemetryCollector.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" . }}-telemetry-collector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: consul-telemetry-collector +rules: + - apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + resourceNames: + - {{ template "consul.fullname" . }}-telemetry-collector + verbs: + - use +{{- end }} + diff --git a/charts/hashicorp/consul/1.5.3/templates/telemetry-collector-rolebinding.yaml b/charts/hashicorp/consul/1.5.3/templates/telemetry-collector-rolebinding.yaml new file mode 100644 index 0000000000..1f9a896997 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/telemetry-collector-rolebinding.yaml @@ -0,0 +1,21 @@ +{{- if and .Values.global.enablePodSecurityPolicies .Values.telemetryCollector.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" . }}-telemetry-collector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: consul-telemetry-collector +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" . }}-telemetry-collector +subjects: + - kind: ServiceAccount + name: {{ template "consul.fullname" . }}-telemetry-collector +{{- end }} + diff --git a/charts/hashicorp/consul/1.5.3/templates/telemetry-collector-service.yaml b/charts/hashicorp/consul/1.5.3/templates/telemetry-collector-service.yaml new file mode 100644 index 0000000000..266c80b4bf --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/telemetry-collector-service.yaml @@ -0,0 +1,24 @@ +{{- if .Values.telemetryCollector.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: consul-telemetry-collector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: consul-telemetry-collector + {{ if .Values.telemetryCollector.service.annotations }} + annotations: + {{ tpl .Values.telemetryCollector.service.annotations . | nindent 4 | trim }} + {{- end }} +spec: + type: ClusterIP + ports: + - port: 9356 + targetPort: 9356 + selector: + app: consul + component: consul-telemetry-collector +{{- end }} \ No newline at end of file diff --git a/charts/hashicorp/consul/1.5.3/templates/telemetry-collector-serviceaccount.yaml b/charts/hashicorp/consul/1.5.3/templates/telemetry-collector-serviceaccount.yaml new file mode 100644 index 0000000000..fca58eede9 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/telemetry-collector-serviceaccount.yaml @@ -0,0 +1,23 @@ +{{- if .Values.telemetryCollector.enabled }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: consul-telemetry-collector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: consul-telemetry-collector + {{- if .Values.telemetryCollector.serviceAccount.annotations }} + annotations: + {{ tpl .Values.telemetryCollector.serviceAccount.annotations . | nindent 4 | trim }} + {{- end }} +automountServiceAccountToken: true +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/hashicorp/consul/1.5.3/templates/telemetry-collector-v2-deployment.yaml b/charts/hashicorp/consul/1.5.3/templates/telemetry-collector-v2-deployment.yaml new file mode 100644 index 0000000000..09f4a2dbbc --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/telemetry-collector-v2-deployment.yaml @@ -0,0 +1,415 @@ +{{- if and .Values.telemetryCollector.enabled (mustHas "resource-apis" .Values.global.experiments) }} +{{- if not .Values.telemetryCollector.image}}{{ fail "telemetryCollector.image must be set to enable consul-telemetry-collector" }}{{ end }} +{{- if not .Values.connectInject.enabled }}{{ fail "connectInject.enabled must be true" }}{{ end -}} +{{- if and .Values.global.adminPartitions.enabled (not .Values.global.enableConsulNamespaces) }}{{ fail "global.enableConsulNamespaces must be true if global.adminPartitions.enabled=true" }}{{ end }} +{{ template "consul.validateCloudSecretKeys" . }} +{{ template "consul.validateTelemetryCollectorCloud" . }} +{{ template "consul.validateTelemetryCollectorCloudSecretKeys" . }} +{{ template "consul.validateTelemetryCollectorResourceId" . }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "consul.fullname" . }}-telemetry-collector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: consul-telemetry-collector + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} +spec: + replicas: {{ .Values.telemetryCollector.replicas }} + selector: + matchLabels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: consul-telemetry-collector + template: + metadata: + annotations: + "consul.hashicorp.com/mesh-inject": "false" + # This annotation tells the pod controller that this pod was injected even though it wasn't. + # This ensures the pod controller will sync a workload for the pod into Consul + "consul.hashicorp.com/mesh-inject-status": "injected" + # We aren't using tproxy and we don't have an original pod. This would be simpler if we made a path similar + # to gateways + "consul.hashicorp.com/transparent-proxy": "false" + "consul.hashicorp.com/transparent-proxy-overwrite-probes": "false" + "consul.hashicorp.com/consul-k8s-version": {{ $.Chart.Version }} + {{- if .Values.telemetryCollector.customExporterConfig }} + # configmap checksum + "consul.hashicorp.com/config-checksum": {{ include (print $.Template.BasePath "/telemetry-collector-configmap.yaml") . | sha256sum }} + {{- end }} + # vault annotations + {{- if (and .Values.global.secretsBackend.vault.enabled .Values.global.tls.enabled) }} + "vault.hashicorp.com/agent-init-first": "true" + "vault.hashicorp.com/agent-inject": "true" + "vault.hashicorp.com/role": {{ .Values.global.secretsBackend.vault.consulCARole }} + "vault.hashicorp.com/agent-inject-secret-serverca.crt": {{ .Values.global.tls.caCert.secretName }} + "vault.hashicorp.com/agent-inject-template-serverca.crt": {{ template "consul.serverTLSCATemplate" . }} + {{- if and .Values.global.secretsBackend.vault.ca.secretName .Values.global.secretsBackend.vault.ca.secretKey }} + "vault.hashicorp.com/agent-extra-secret": "{{ .Values.global.secretsBackend.vault.ca.secretName }}" + "vault.hashicorp.com/ca-cert": "/vault/custom/{{ .Values.global.secretsBackend.vault.ca.secretKey }}" + {{- end }} + {{- if .Values.global.secretsBackend.vault.agentAnnotations }} + {{ tpl .Values.global.secretsBackend.vault.agentAnnotations . | nindent 8 | trim }} + {{- end }} + {{- if (and (.Values.global.secretsBackend.vault.vaultNamespace) (not (hasKey (default "" .Values.global.secretsBackend.vault.agentAnnotations | fromYaml) "vault.hashicorp.com/namespace")))}} + "vault.hashicorp.com/namespace": "{{ .Values.global.secretsBackend.vault.vaultNamespace }}" + {{- end }} + {{- end }} + + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: consul-telemetry-collector + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} + spec: + # This needs to explicitly be consul-telemetry-collector because we look this up from each service consul-dataplane + # to forward metrics to it. + serviceAccountName: consul-telemetry-collector + initContainers: + # We're manually managing this init container instead of using the mesh injector so that we don't run into + # any race conditions on the mesh-injector deployment or upgrade + - name: consul-mesh-init + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + # acl login info + {{- if .Values.global.acls.manageSystemACLs }} + - name: CONSUL_LOGIN_AUTH_METHOD + value: {{ template "consul.fullname" . }}-k8s-auth-method + - name: CONSUL_LOGIN_DATACENTER + value: {{ .Values.global.datacenter }} + - name: CONSUL_LOGIN_META + value: "component=consul-telemetry-collector,pod=$(NAMESPACE)/$(POD_NAME)" + {{- end }} + # service and login namespace + # this is attempting to replicate the behavior of webhooks in calculating namespace + # https://github.com/hashicorp/consul-k8s/blob/b84339050bb2c4b62b60cec96275f74952b0ac9d/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go#L200 + {{- if .Values.global.enableConsulNamespaces }} + {{- if .Values.connectInject.consulNamespaces.mirroringK8S }} + - name: CONSUL_NAMESPACE + value: {{ .Values.connectInject.consulNamespaces.mirroringK8SPrefix }}{{ .Release.Namespace }} + {{- else }} + - name: CONSUL_NAMESPACE + value: {{ .Values.connectInject.consulNamespaces.consulDestinationNamespace }} + {{- end }} + {{- if .Values.global.acls.manageSystemACLs }} + {{- if .Values.connectInject.consulNamespaces.mirroringK8S }} + - name: CONSUL_LOGIN_NAMESPACE + value: "default" + {{- else }} + - name: CONSUL_LOGIN_NAMESPACE + value: {{ .Values.connectInject.consulNamespaces.consulDestinationNamespace }} + {{- end }} + {{- end }} + {{- end }} + command: + - /bin/sh + - -ec + - |- + exec consul-k8s-control-plane mesh-init -proxy-name=${POD_NAME} \ + -log-level={{ default .Values.global.logLevel .Values.telemetryCollector.logLevel }} \ + -log-json={{ .Values.global.logJSON }} + + image: {{ .Values.global.imageK8S }} + imagePullPolicy: IfNotPresent + {{- if .Values.telemetryCollector.initContainer.resources }} + resources: + {{- toYaml .Values.telemetryCollector.initContainer.resources | nindent 12 }} + {{- else }} + resources: + limits: + cpu: 50m + memory: 150Mi + requests: + cpu: 50m + memory: 25Mi + {{- end }} + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /consul/mesh-inject + name: consul-mesh-inject-data + {{- if .Values.global.tls.enabled }} + {{- if not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled) }} + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- end }} + containers: + - name: consul-telemetry-collector + image: {{ .Values.telemetryCollector.image }} + imagePullPolicy: {{ .Values.global.imagePullPolicy }} + ports: + - containerPort: 9090 + name: metrics + protocol: TCP + - containerPort: 9356 + name: metricsserver + protocol: TCP + env: + # These are mounted as secrets so that the telemetry-collector can use them when cloud is enabled. + # - the hcp-go-sdk in consul agent will already look for HCP_CLIENT_ID, HCP_CLIENT_SECRET, HCP_AUTH_URL, + # HCP_SCADA_ADDRESS, and HCP_API_HOST. so nothing more needs to be done. + # - HCP_RESOURCE_ID is created either in the global cloud section or in telemetryCollector.cloud + {{- if .Values.telemetryCollector.cloud.resourceId.secretName }} + - name: HCP_RESOURCE_ID + valueFrom: + secretKeyRef: + name: {{ .Values.telemetryCollector.cloud.resourceId.secretName }} + key: {{ .Values.telemetryCollector.cloud.resourceId.secretKey }} + {{- else if .Values.global.cloud.resourceId.secretName }} + - name: HCP_RESOURCE_ID + valueFrom: + secretKeyRef: + name: {{ .Values.global.cloud.resourceId.secretName }} + key: {{ .Values.global.cloud.resourceId.secretKey }} + {{- end }} + {{- if .Values.telemetryCollector.cloud.clientId.secretName }} + - name: HCP_CLIENT_ID + valueFrom: + secretKeyRef: + name: {{ .Values.telemetryCollector.cloud.clientId.secretName }} + key: {{ .Values.telemetryCollector.cloud.clientId.secretKey }} + {{- else if .Values.global.cloud.clientId.secretName }} + - name: HCP_CLIENT_ID + valueFrom: + secretKeyRef: + name: {{ .Values.global.cloud.clientId.secretName }} + key: {{ .Values.global.cloud.clientId.secretKey }} + {{- end }} + {{- if .Values.telemetryCollector.cloud.clientSecret.secretName }} + - name: HCP_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: {{ .Values.telemetryCollector.cloud.clientSecret.secretName }} + key: {{ .Values.telemetryCollector.cloud.clientSecret.secretKey }} + {{- else if .Values.global.cloud.clientSecret.secretName }} + - name: HCP_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: {{ .Values.global.cloud.clientSecret.secretName }} + key: {{ .Values.global.cloud.clientSecret.secretKey }} + {{- end}} + {{- if .Values.global.cloud.authUrl.secretName }} + - name: HCP_AUTH_URL + valueFrom: + secretKeyRef: + name: {{ .Values.global.cloud.authUrl.secretName }} + key: {{ .Values.global.cloud.authUrl.secretKey }} + {{- end}} + {{- if .Values.global.cloud.apiHost.secretName }} + - name: HCP_API_HOST + valueFrom: + secretKeyRef: + name: {{ .Values.global.cloud.apiHost.secretName }} + key: {{ .Values.global.cloud.apiHost.secretKey }} + {{- end}} + {{- if .Values.global.cloud.scadaAddress.secretName }} + - name: HCP_SCADA_ADDRESS + valueFrom: + secretKeyRef: + name: {{ .Values.global.cloud.scadaAddress.secretName }} + key: {{ .Values.global.cloud.scadaAddress.secretKey }} + {{- end}} + {{- if .Values.global.trustedCAs }} + - name: SSL_CERT_DIR + value: "/etc/ssl/certs:/trusted-cas" + {{- end }} + {{- include "consul.extraEnvironmentVars" .Values.telemetryCollector | nindent 12 }} + command: + - "/bin/sh" + - "-ec" + - | + {{- if .Values.global.trustedCAs }} + {{- range $i, $cert := .Values.global.trustedCAs }} + cat < /trusted-cas/custom-ca-{{$i}}.pem + {{- $cert | nindent 10 }} + EOF + {{- end }} + {{- end }} + + exec consul-telemetry-collector agent \ + {{- if .Values.telemetryCollector.customExporterConfig }} + -config-file-path /consul/config/config.json \ + {{ end }} + volumeMounts: + {{- if .Values.telemetryCollector.customExporterConfig }} + - name: config + mountPath: /consul/config + {{- end }} + {{- if .Values.global.trustedCAs }} + - name: trusted-cas + mountPath: /trusted-cas + readOnly: false + {{- end }} + resources: + {{- if .Values.telemetryCollector.resources }} + {{- toYaml .Values.telemetryCollector.resources | nindent 12 }} + {{- end }} + # consul-dataplane container + - name: consul-dataplane + image: "{{ .Values.global.imageConsulDataplane }}" + imagePullPolicy: IfNotPresent + command: + - consul-dataplane + args: + # addresses + {{- if .Values.externalServers.enabled }} + - -addresses={{ .Values.externalServers.hosts | first }} + {{- else }} + - -addresses={{ template "consul.fullname" . }}-server.{{ .Release.Namespace }}.svc + {{- end }} + # grpc + {{- if .Values.externalServers.enabled }} + - -grpc-port={{ .Values.externalServers.grpcPort }} + {{- else }} + - -grpc-port=8502 + {{- end }} + # tls + {{- if .Values.global.tls.enabled }} + {{- if (not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots)) }} + {{- if .Values.global.secretsBackend.vault.enabled }} + - -ca-certs=/vault/secrets/serverca.crt + {{- else }} + - -ca-certs=/consul/tls/ca/tls.crt + {{- end }} + {{- end }} + {{- if and .Values.externalServers.enabled .Values.externalServers.tlsServerName }} + - -tls-server-name={{.Values.externalServers.tlsServerName }} + {{- else if .Values.global.cloud.enabled }} + - -tls-server-name=server.{{ .Values.global.datacenter}}.{{ .Values.global.domain}} + {{- end }} + {{- else }} + - -tls-disabled + {{- end }} + # credentials + {{- if .Values.global.acls.manageSystemACLs }} + - -credential-type=login + - -login-bearer-token-path=/var/run/secrets/kubernetes.io/serviceaccount/token + - -login-auth-method={{ template "consul.fullname" . }}-k8s-auth-method + {{- end }} + # service and login namespace + {{- if .Values.global.enableConsulNamespaces }} + {{- if .Values.connectInject.consulNamespaces.mirroringK8S }} + - -service-namespace={{ .Values.connectInject.consulNamespaces.mirroringK8SPrefix }}{{ .Release.Namespace }} + {{- else }} + - -service-namespace={{ .Values.connectInject.consulNamespaces.consulDestinationNamespace }} + {{- end }} + {{- if .Values.global.acls.manageSystemACLs }} + {{- if .Values.connectInject.consulNamespaces.mirroringK8S }} + - -login-namespace=default + {{- else }} + - -login-namespace={{ .Values.connectInject.consulNamespaces.consulDestinationNamespace }} + {{- end }} + {{- end }} + {{- end }} + # service and login partition + {{- if .Values.global.adminPartitions.enabled }} + - -service-partition={{ .Values.global.adminPartitions.name }} + {{- if .Values.global.acls.manageSystemACLs }} + - -login-partition={{ .Values.global.adminPartitions.name }} + {{- end }} + {{- end }} + # telemetry + {{- if .Values.global.metrics.enabled }} + - -telemetry-prom-scrape-path=/metrics + {{- end }} + - -log-level={{ default .Values.global.logLevel .Values.telemetryCollector.logLevel }} + - -log-json={{ .Values.global.logJSON }} + - -envoy-concurrency=2 + {{- if and .Values.externalServers.enabled .Values.externalServers.skipServerWatch }} + - -server-watch-disabled=true + {{- end }} + env: + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: DP_PROXY_ID + value: $(POD_NAME) + - name: DP_CREDENTIAL_LOGIN_META1 + value: pod=$(NAMESPACE)/$(POD_NAME) + - name: DP_CREDENTIAL_LOGIN_META2 + value: component=consul-telemetry-collector + - name: TMPDIR + value: /consul/mesh-inject + readinessProbe: + failureThreshold: 3 + initialDelaySeconds: 1 + periodSeconds: 10 + successThreshold: 1 + tcpSocket: + port: 20000 + timeoutSeconds: 1 + securityContext: + readOnlyRootFilesystem: true + runAsGroup: 5995 + runAsNonRoot: true + runAsUser: 5995 + # dataplane volume mounts + volumeMounts: + - mountPath: /consul/mesh-inject + name: consul-mesh-inject-data + {{- if .Values.global.tls.enabled }} + {{- if not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled) }} + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- end }} + + {{- if .Values.telemetryCollector.nodeSelector }} + nodeSelector: + {{ tpl .Values.telemetryCollector.nodeSelector . | indent 8 | trim }} + {{- end }} + {{- if .Values.telemetryCollector.priorityClassName }} + priorityClassName: {{ .Values.telemetryCollector.priorityClassName }} + {{- end }} + volumes: + - emptyDir: + medium: Memory + name: consul-mesh-inject-data + {{- if .Values.global.trustedCAs }} + - name: trusted-cas + emptyDir: + medium: "Memory" + {{- end }} + {{- if .Values.global.tls.enabled }} + {{- if not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled) }} + - name: consul-ca-cert + secret: + {{- if .Values.global.tls.caCert.secretName }} + secretName: {{ .Values.global.tls.caCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" . }}-ca-cert + {{- end }} + items: + - key: {{ default "tls.crt" .Values.global.tls.caCert.secretKey }} + path: tls.crt + {{- end }} + {{- end }} + - name: config + configMap: + name: {{ template "consul.fullname" . }}-telemetry-collector +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/terminating-gateways-deployment.yaml b/charts/hashicorp/consul/1.5.3/templates/terminating-gateways-deployment.yaml new file mode 100644 index 0000000000..c4970979b1 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/terminating-gateways-deployment.yaml @@ -0,0 +1,356 @@ +{{- if .Values.terminatingGateways.enabled }} +{{- if not .Values.connectInject.enabled }}{{ fail "connectInject.enabled must be true" }}{{ end -}} +{{- if and .Values.global.adminPartitions.enabled (not .Values.global.enableConsulNamespaces) }}{{ fail "global.enableConsulNamespaces must be true if global.adminPartitions.enabled=true" }}{{ end }} +{{ template "consul.validateRequiredCloudSecretsExist" . }} +{{ template "consul.validateCloudSecretKeys" . }} + +{{- $root := . }} +{{- $defaults := .Values.terminatingGateways.defaults }} +{{- $names := dict }} + +{{- $gateways := .Values.terminatingGateways.gateways }} +{{- range $outerTerminatingIndex, $outerTerminatingVal := $gateways }} + +{{- range $innerTerminatingIndex, $innerTerminatingVal := $gateways }} +{{- if (and (ne $outerTerminatingIndex $innerTerminatingIndex) (eq $outerTerminatingVal.name $innerTerminatingVal.name)) }} +{{ fail (cat "terminating gateways must have unique names but found duplicate name" $innerTerminatingVal.name) }} +{{ end -}} +{{ end -}} + +{{- range $outerIngressIndex, $outerIngressVal := $root.Values.ingressGateways.gateways }} +{{- if (eq $outerTerminatingVal.name $outerIngressVal.name) }} +{{ fail (cat "terminating gateways cannot have duplicate names of any ingress gateways but found duplicate name" $outerTerminatingVal.name) }} +{{ end -}} +{{ end -}} +{{ end -}} + +{{- range .Values.terminatingGateways.gateways }} + +{{- if empty .name }} +# Check that name is not empty +{{ fail "Terminating gateway names cannot be empty"}} +{{ end -}} +{{- if hasKey $names .name }} +# Check that the name doesn't already exist +{{ fail "Terminating gateway names must be unique"}} +{{ end -}} +{{- /* Add the gateway name to the $names dict to ensure uniqueness */ -}} +{{- $_ := set $names .name .name }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "consul.fullname" $root }}-{{ .name }} + namespace: {{ $root.Release.Namespace }} + labels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: terminating-gateway + terminating-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} + {{- if $root.Values.global.extraLabels }} + {{- toYaml $root.Values.global.extraLabels | nindent 4 }} + {{- end }} +spec: + replicas: {{ default $defaults.replicas .replicas }} + selector: + matchLabels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: terminating-gateway + terminating-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} + template: + metadata: + labels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: terminating-gateway + terminating-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} + consul.hashicorp.com/connect-inject-managed-by: consul-k8s-endpoints-controller + {{- if $root.Values.global.extraLabels }} + {{- toYaml $root.Values.global.extraLabels | nindent 8 }} + {{- end }} + annotations: + "consul.hashicorp.com/connect-inject": "false" + "consul.hashicorp.com/mesh-inject": "false" + "consul.hashicorp.com/gateway-kind": "terminating-gateway" + "consul.hashicorp.com/gateway-consul-service-name": "{{ .name }}" + {{- if $root.Values.global.enableConsulNamespaces }} + "consul.hashicorp.com/gateway-namespace": {{ (default $defaults.consulNamespace .consulNamespace) }} + {{- end }} + {{- if (and $root.Values.global.secretsBackend.vault.enabled $root.Values.global.tls.enabled) }} + "vault.hashicorp.com/agent-init-first": "true" + "vault.hashicorp.com/agent-inject": "true" + "vault.hashicorp.com/role": {{ $root.Values.global.secretsBackend.vault.consulCARole }} + "vault.hashicorp.com/agent-inject-secret-serverca.crt": {{ $root.Values.global.tls.caCert.secretName }} + "vault.hashicorp.com/agent-inject-template-serverca.crt": {{ template "consul.serverTLSCATemplate" $root }} + {{- if and $root.Values.global.secretsBackend.vault.ca.secretName $root.Values.global.secretsBackend.vault.ca.secretKey }} + "vault.hashicorp.com/agent-extra-secret": {{ $root.Values.global.secretsBackend.vault.ca.secretName }} + "vault.hashicorp.com/ca-cert": /vault/custom/{{ $root.Values.global.secretsBackend.vault.ca.secretKey }} + {{- end }} + {{- if $root.Values.global.secretsBackend.vault.agentAnnotations }} + {{ tpl $root.Values.global.secretsBackend.vault.agentAnnotations $root | nindent 8 | trim }} + {{- end }} + {{- if (and ($root.Values.global.secretsBackend.vault.vaultNamespace) (not (hasKey (default "" $root.Values.global.secretsBackend.vault.agentAnnotations | fromYaml) "vault.hashicorp.com/namespace")))}} + "vault.hashicorp.com/namespace": "{{ $root.Values.global.secretsBackend.vault.vaultNamespace }}" + {{- end }} + {{- end }} + {{- if (and $root.Values.global.metrics.enabled $root.Values.global.metrics.enableGatewayMetrics) }} + "prometheus.io/scrape": "true" + {{- if not (hasKey (default "" $defaults.annotations | fromYaml) "prometheus.io/path")}} + "prometheus.io/path": "/metrics" + {{- end }} + "prometheus.io/port": "20200" + {{- end }} + {{- if $defaults.annotations }} + # We allow both default annotations and gateway-specific annotations + {{- tpl $defaults.annotations $root | nindent 8 }} + {{- end }} + {{- if .annotations }} + {{- tpl .annotations $root | nindent 8 }} + {{- end }} + spec: + {{- if (or $defaults.affinity .affinity) }} + affinity: + {{ tpl (default $defaults.affinity .affinity) $root | nindent 8 | trim }} + {{- end }} + {{- if (or $defaults.tolerations .tolerations) }} + tolerations: + {{ tpl (default $defaults.tolerations .tolerations) $root | nindent 8 | trim }} + {{- end }} + {{- if (or $defaults.topologySpreadConstraints .topologySpreadConstraints) }} + topologySpreadConstraints: + {{ tpl (default $defaults.topologySpreadConstraints .topologySpreadConstraints) $root | nindent 8 | trim }} + {{- end }} + terminationGracePeriodSeconds: 10 + serviceAccountName: {{ template "consul.fullname" $root }}-{{ .name }} + volumes: + - name: tmp + emptyDir: + medium: "Memory" + - name: consul-service + emptyDir: + medium: "Memory" + {{- range (default $defaults.extraVolumes .extraVolumes) }} + - name: userconfig-{{ .name }} + {{ .type }}: + {{- if (eq .type "configMap") }} + name: {{ .name }} + {{- else if (eq .type "secret") }} + secretName: {{ .name }} + {{- end }} + {{- with .items }} + items: + {{- range . }} + - key: {{.key}} + path: {{.path}} + {{- end }} + {{- end }} + {{- end }} + {{- if $root.Values.global.tls.enabled }} + {{- if not (or (and $root.Values.externalServers.enabled $root.Values.externalServers.useSystemRoots) ($root.Values.global.secretsBackend.vault.enabled)) }} + - name: consul-ca-cert + secret: + {{- if $root.Values.global.tls.caCert.secretName }} + secretName: {{ $root.Values.global.tls.caCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" $root }}-ca-cert + {{- end }} + items: + - key: {{ default "tls.crt" $root.Values.global.tls.caCert.secretKey }} + path: tls.crt + {{- end }} + {{- end }} + initContainers: + # terminating-gateway-init registers the terminating gateway service with Consul. + - name: terminating-gateway-init + image: {{ $root.Values.global.imageK8S }} + {{ template "consul.imagePullPolicy" $root }} + {{- include "consul.restrictedSecurityContext" $ | nindent 10 }} + env: + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + {{- include "consul.consulK8sConsulServerEnvVars" $root | nindent 10 }} + {{- if $root.Values.global.enableConsulNamespaces }} + - name: CONSUL_NAMESPACE + value: {{ (default $defaults.consulNamespace .consulNamespace) }} + {{- end }} + {{- if $root.Values.global.acls.manageSystemACLs }} + - name: CONSUL_LOGIN_AUTH_METHOD + value: {{ template "consul.fullname" $root }}-k8s-component-auth-method + - name: CONSUL_LOGIN_DATACENTER + value: {{ $root.Values.global.datacenter }} + - name: CONSUL_LOGIN_META + value: "component=terminating-gateway,pod=$(NAMESPACE)/$(POD_NAME)" + {{- end }} + - name: CONSUL_NODE_NAME + value: $(NODE_NAME)-virtual + command: + - "/bin/sh" + - "-ec" + - | + exec consul-k8s-control-plane connect-init -pod-name=${POD_NAME} -pod-namespace=${NAMESPACE} \ + -gateway-kind="terminating-gateway" \ + -proxy-id-file=/consul/service/proxy-id \ + -service-name={{ .name }} \ + -log-level={{ default $root.Values.global.logLevel $root.Values.terminatingGateways.logLevel }} \ + -log-json={{ $root.Values.global.logJSON }} + volumeMounts: + - name: tmp + mountPath: /tmp + - name: consul-service + mountPath: /consul/service + {{- if $root.Values.global.tls.enabled }} + {{- if not (or (and $root.Values.externalServers.enabled $root.Values.externalServers.useSystemRoots) ($root.Values.global.secretsBackend.vault.enabled)) }} + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- end }} + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" + containers: + - name: terminating-gateway + image: {{ $root.Values.global.imageConsulDataplane | quote }} + {{ template "consul.imagePullPolicy" $root }} + {{- include "consul.restrictedSecurityContext" $ | nindent 10 }} + volumeMounts: + - name: tmp + mountPath: /tmp + - name: consul-service + mountPath: /consul/service + readOnly: true + {{- if $root.Values.global.tls.enabled }} + {{- if not (or (and $root.Values.externalServers.enabled $root.Values.externalServers.useSystemRoots) ($root.Values.global.secretsBackend.vault.enabled)) }} + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- end }} + {{- range (default $defaults.extraVolumes .extraVolumes) }} + - name: userconfig-{{ .name }} + readOnly: true + mountPath: /consul/userconfig/{{ .name }} + {{- end }} + {{- if (default $defaults.resources .resources) }} + resources: {{ toYaml (default $defaults.resources .resources) | nindent 12 }} + {{- end }} + env: + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: DP_CREDENTIAL_LOGIN_META1 + value: pod=$(NAMESPACE)/$(POD_NAME) + - name: DP_CREDENTIAL_LOGIN_META2 + value: component=terminating-gateway + - name: DP_SERVICE_NODE_NAME + value: $(NODE_NAME)-virtual + command: + - consul-dataplane + args: + {{- if $root.Values.externalServers.enabled }} + - -addresses={{ $root.Values.externalServers.hosts | first }} + {{- else }} + - -addresses={{ template "consul.fullname" $root }}-server.{{ $root.Release.Namespace }}.svc + {{- end }} + {{- if $root.Values.externalServers.enabled }} + - -grpc-port={{ $root.Values.externalServers.grpcPort }} + {{- else }} + - -grpc-port=8502 + {{- end }} + - -proxy-service-id-path=/consul/service/proxy-id + {{- if $root.Values.global.enableConsulNamespaces }} + - -service-namespace={{ (default $defaults.consulNamespace .consulNamespace) }} + {{- end }} + {{- if and $root.Values.global.tls.enabled }} + {{- if (not (and $root.Values.externalServers.enabled $root.Values.externalServers.useSystemRoots)) }} + {{- if $root.Values.global.secretsBackend.vault.enabled }} + - -ca-certs=/vault/secrets/serverca.crt + {{- else }} + - -ca-certs=/consul/tls/ca/tls.crt + {{- end }} + {{- end }} + {{- if and $root.Values.externalServers.enabled $root.Values.externalServers.tlsServerName }} + - -tls-server-name={{$root.Values.externalServers.tlsServerName }} + {{- else if $root.Values.global.cloud.enabled }} + - -tls-server-name=server.{{ $root.Values.global.datacenter}}.{{ $root.Values.global.domain}} + {{- end }} + {{- else }} + - -tls-disabled + {{- end }} + {{- if $root.Values.global.acls.manageSystemACLs }} + - -credential-type=login + - -login-bearer-token-path=/var/run/secrets/kubernetes.io/serviceaccount/token + - -login-auth-method={{ template "consul.fullname" $root }}-k8s-component-auth-method + {{- if $root.Values.global.adminPartitions.enabled }} + - -login-partition={{ $root.Values.global.adminPartitions.name }} + {{- end }} + {{- end }} + {{- if $root.Values.global.adminPartitions.enabled }} + - -service-partition={{ $root.Values.global.adminPartitions.name }} + {{- end }} + - -log-level={{ default $root.Values.global.logLevel $root.Values.terminatingGateways.logLevel }} + - -log-json={{ $root.Values.global.logJSON }} + {{- if (and $root.Values.global.metrics.enabled $root.Values.global.metrics.enableGatewayMetrics) }} + - -telemetry-prom-scrape-path=/metrics + {{- end }} + {{- if and $root.Values.externalServers.enabled $root.Values.externalServers.skipServerWatch }} + - -server-watch-disabled=true + {{- end }} + livenessProbe: + tcpSocket: + port: 8443 + failureThreshold: 3 + initialDelaySeconds: 30 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + readinessProbe: + tcpSocket: + port: 8443 + failureThreshold: 3 + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + ports: + - name: gateway + containerPort: 8443 + {{- if (default $defaults.priorityClassName .priorityClassName) }} + priorityClassName: {{ (default $defaults.priorityClassName .priorityClassName) | quote }} + {{- end }} + {{- if (default $defaults.nodeSelector .nodeSelector) }} + nodeSelector: + {{ tpl (default $defaults.nodeSelector .nodeSelector) $root | indent 8 | trim }} + {{- end }} +--- +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/terminating-gateways-podsecuritypolicy.yaml b/charts/hashicorp/consul/1.5.3/templates/terminating-gateways-podsecuritypolicy.yaml new file mode 100644 index 0000000000..7307fb8be9 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/terminating-gateways-podsecuritypolicy.yaml @@ -0,0 +1,47 @@ +{{- if (and .Values.global.enablePodSecurityPolicies .Values.terminatingGateways.enabled) }} +{{- $root := . }} +{{- range .Values.terminatingGateways.gateways }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" $root }}-{{ .name }} + namespace: {{ $root.Release.Namespace }} + labels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: terminating-gateway + terminating-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + defaultAddCapabilities: + - NET_BIND_SERVICE + # Allow core volume types. + volumes: + - 'configMap' + - 'emptyDir' + - 'projected' + - 'secret' + - 'downwardAPI' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +--- +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/terminating-gateways-role.yaml b/charts/hashicorp/consul/1.5.3/templates/terminating-gateways-role.yaml new file mode 100644 index 0000000000..51e37664d3 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/terminating-gateways-role.yaml @@ -0,0 +1,32 @@ +{{- if .Values.terminatingGateways.enabled }} + +{{- $root := . }} +{{- $defaults := .Values.terminatingGateways.defaults }} + +{{- range .Values.terminatingGateways.gateways }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" $root }}-{{ .name }} + namespace: {{ $root.Release.Namespace }} + labels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: terminating-gateway + terminating-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} +{{- if $root.Values.global.enablePodSecurityPolicies }} +rules: + - apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + resourceNames: + - {{ template "consul.fullname" $root }}-{{ .name }} + verbs: + - use +{{- else }} +rules: [] +{{- end }} +--- +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/terminating-gateways-rolebinding.yaml b/charts/hashicorp/consul/1.5.3/templates/terminating-gateways-rolebinding.yaml new file mode 100644 index 0000000000..4271f8f59c --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/terminating-gateways-rolebinding.yaml @@ -0,0 +1,26 @@ +{{- if .Values.terminatingGateways.enabled }} +{{- $root := . }} +{{- range .Values.terminatingGateways.gateways }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" $root }}-{{ .name }} + namespace: {{ $root.Release.Namespace }} + labels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: terminating-gateway + terminating-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" $root }}-{{ .name }} +subjects: + - kind: ServiceAccount + name: {{ template "consul.fullname" $root }}-{{ .name }} + namespace: {{ $root.Release.Namespace }} +--- +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/terminating-gateways-service.yaml b/charts/hashicorp/consul/1.5.3/templates/terminating-gateways-service.yaml new file mode 100644 index 0000000000..124900e727 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/terminating-gateways-service.yaml @@ -0,0 +1,31 @@ +{{- if .Values.terminatingGateways.enabled }} + +{{- $root := . }} +{{- $defaults := .Values.terminatingGateways.defaults }} + +{{- range .Values.terminatingGateways.gateways }} + +{{- $service := .service }} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "consul.fullname" $root }}-{{ .name }} + namespace: {{ $root.Release.Namespace }} + labels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: terminating-gateway +spec: + selector: + app: {{ template "consul.name" $root }} + release: "{{ $root.Release.Name }}" + component: terminating-gateway + type: ClusterIP + ports: + - port: 80 + targetPort: 8443 +--- +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/terminating-gateways-serviceaccount.yaml b/charts/hashicorp/consul/1.5.3/templates/terminating-gateways-serviceaccount.yaml new file mode 100644 index 0000000000..211fb5c72f --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/terminating-gateways-serviceaccount.yaml @@ -0,0 +1,35 @@ +{{- if .Values.terminatingGateways.enabled }} +{{- $root := . }} +{{- $defaults := .Values.terminatingGateways.defaults }} +{{- range .Values.terminatingGateways.gateways }} +{{- $serviceAccount := .serviceAccount }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" $root }}-{{ .name }} + namespace: {{ $root.Release.Namespace }} + labels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: terminating-gateway + terminating-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} + {{- if (or $defaults.serviceAccount.annotations $serviceAccount.annotations) }} + annotations: + {{- if $defaults.serviceAccount.annotations }} + {{ tpl $defaults.serviceAccount.annotations $root | nindent 4 | trim }} + {{- end }} + {{- if $serviceAccount.annotations }} + {{ tpl $serviceAccount.annotations $root | nindent 4 | trim }} + {{- end }} + {{- end }} +{{- with $root.Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +--- +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/tests/test-runner.yaml b/charts/hashicorp/consul/1.5.3/templates/tests/test-runner.yaml new file mode 100644 index 0000000000..4c3e81ccea --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/tests/test-runner.yaml @@ -0,0 +1,79 @@ +{{- if .Values.tests.enabled }} +apiVersion: v1 +kind: Pod +metadata: + name: "{{ template "consul.fullname" . }}-test" + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + annotations: + "helm.sh/hook": test-success +spec: + {{- if .Values.global.tls.enabled }} + volumes: + {{- if not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) }} + - name: consul-ca-cert + secret: + {{- if .Values.global.tls.caCert.secretName }} + secretName: {{ .Values.global.tls.caCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" . }}-ca-cert + {{- end }} + items: + - key: {{ default "tls.crt" .Values.global.tls.caCert.secretKey }} + path: tls.crt + {{- end }} + - name: consul-auto-encrypt-ca-cert + emptyDir: + medium: "Memory" + {{- end }} + {{- if and .Values.global.tls.enabled .Values.global.tls.enableAutoEncrypt }} + initContainers: + {{- include "consul.getAutoEncryptClientCA" . | nindent 2 }} + {{- end }} + containers: + - name: consul-test + image: "{{ .Values.global.image }}" + {{ template "consul.imagePullPolicy" . }} + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + {{- if .Values.global.tls.enabled }} + - name: CONSUL_HTTP_ADDR + value: https://$(HOST_IP):8501 + - name: CONSUL_CACERT + value: /consul/tls/ca/tls.crt + {{- else }} + - name: CONSUL_HTTP_ADDR + value: http://$(HOST_IP):8500 + {{- end }} + {{- if .Values.global.tls.enabled }} + volumeMounts: + {{- if .Values.global.tls.enableAutoEncrypt }} + - name: consul-auto-encrypt-ca-cert + mountPath: /consul/tls/ca + readOnly: true + {{- else }} + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- end }} + command: + - "/bin/sh" + - "-ec" + - | + consul members | tee members.txt + if [ $(grep -c consul-server members.txt) != $(grep consul-server members.txt | grep -c alive) ] + then + echo "Failed because not all consul servers are available" + exit 1 + fi + + restartPolicy: Never +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/tls-init-cleanup-job.yaml b/charts/hashicorp/consul/1.5.3/templates/tls-init-cleanup-job.yaml new file mode 100644 index 0000000000..5ebe236df5 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/tls-init-cleanup-job.yaml @@ -0,0 +1,85 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} +{{- if not .Values.global.secretsBackend.vault.enabled }} +# tls-init-cleanup job deletes Kubernetes secrets created by tls-init +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "consul.fullname" . }}-tls-init-cleanup + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: tls-init-cleanup + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} + annotations: + "helm.sh/hook": pre-delete + "helm.sh/hook-delete-policy": hook-succeeded + {{- /* Hook weight needs to be 1 so that the service account is provisioned first */}} + "helm.sh/hook-weight": "1" +spec: + template: + metadata: + name: {{ template "consul.fullname" . }}-tls-init-cleanup + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: tls-init-cleanup + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} + annotations: + "consul.hashicorp.com/connect-inject": "false" + "consul.hashicorp.com/mesh-inject": "false" + {{- if .Values.global.tls.annotations }} + {{- tpl .Values.global.tls.annotations . | nindent 8 }} + {{- end }} + spec: + restartPolicy: Never + serviceAccountName: {{ template "consul.fullname" . }}-tls-init-cleanup + {{- if .Values.server.containerSecurityContext.tlsInit }} + securityContext: + {{- toYaml .Values.server.containerSecurityContext.tlsInit | nindent 8 }} + {{- end }} + containers: + - name: tls-init-cleanup + image: "{{ .Values.global.image }}" + {{ template "consul.imagePullPolicy" . }} + {{- if not .Values.server.containerSecurityContext.tlsInit }} + {{- include "consul.restrictedSecurityContext" . | nindent 10 }} + {{- end }} + env: + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + command: + - "/bin/sh" + - "-ec" + - | + {{- if (not (and .Values.global.tls.caCert.secretName .Values.global.tls.caKey.secretName)) }} + curl -s -X DELETE --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \ + https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT}/api/v1/namespaces/${NAMESPACE}/secrets/{{ template "consul.fullname" . }}-ca-cert \ + -H "Authorization: Bearer $( cat /var/run/secrets/kubernetes.io/serviceaccount/token )" + curl -s -X DELETE --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \ + https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT}/api/v1/namespaces/${NAMESPACE}/secrets/{{ template "consul.fullname" . }}-ca-key \ + -H "Authorization: Bearer $( cat /var/run/secrets/kubernetes.io/serviceaccount/token )" + {{- end }} + curl -s -X DELETE --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \ + https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT}/api/v1/namespaces/${NAMESPACE}/secrets/{{ template "consul.fullname" . }}-server-cert \ + -H "Authorization: Bearer $( cat /var/run/secrets/kubernetes.io/serviceaccount/token )" + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/tls-init-cleanup-podsecuritypolicy.yaml b/charts/hashicorp/consul/1.5.3/templates/tls-init-cleanup-podsecuritypolicy.yaml new file mode 100644 index 0000000000..ed99d5f297 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/tls-init-cleanup-podsecuritypolicy.yaml @@ -0,0 +1,43 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (and (and .Values.global.tls.enabled .Values.global.enablePodSecurityPolicies) (not .Values.server.serverCert.secretName)) }} +{{- if not .Values.global.secretsBackend.vault.enabled }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-tls-init-cleanup + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: tls-init-cleanup + annotations: + "helm.sh/hook": pre-delete + "helm.sh/hook-delete-policy": hook-succeeded +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + # Allow core volume types. + volumes: + - 'secret' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/tls-init-cleanup-role.yaml b/charts/hashicorp/consul/1.5.3/templates/tls-init-cleanup-role.yaml new file mode 100644 index 0000000000..aa66e3edc4 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/tls-init-cleanup-role.yaml @@ -0,0 +1,41 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} +{{- if not .Values.global.secretsBackend.vault.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" . }}-tls-init-cleanup + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: tls-init-cleanup + annotations: + "helm.sh/hook": pre-delete + "helm.sh/hook-delete-policy": hook-succeeded +rules: +- apiGroups: [""] + resources: + - secrets + resourceNames: + {{- if (not (and .Values.global.tls.caCert.secretName .Values.global.tls.caKey.secretName)) }} + - {{ template "consul.fullname" . }}-ca-cert + - {{ template "consul.fullname" . }}-ca-key + {{- end }} + - {{ template "consul.fullname" . }}-server-cert + verbs: + - delete +{{- if .Values.global.enablePodSecurityPolicies }} +- apiGroups: ["policy"] + resources: + - podsecuritypolicies + verbs: + - use + resourceNames: + - {{ template "consul.fullname" . }}-tls-init-cleanup +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/tls-init-cleanup-rolebinding.yaml b/charts/hashicorp/consul/1.5.3/templates/tls-init-cleanup-rolebinding.yaml new file mode 100644 index 0000000000..0d3bfe38e7 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/tls-init-cleanup-rolebinding.yaml @@ -0,0 +1,27 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} +{{- if not .Values.global.secretsBackend.vault.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" . }}-tls-init-cleanup + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: tls-init-cleanup + annotations: + "helm.sh/hook": pre-delete + "helm.sh/hook-delete-policy": hook-succeeded +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" . }}-tls-init-cleanup +subjects: +- kind: ServiceAccount + name: {{ template "consul.fullname" . }}-tls-init-cleanup +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/tls-init-cleanup-serviceaccount.yaml b/charts/hashicorp/consul/1.5.3/templates/tls-init-cleanup-serviceaccount.yaml new file mode 100644 index 0000000000..57e40dd3af --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/tls-init-cleanup-serviceaccount.yaml @@ -0,0 +1,26 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} +{{- if not .Values.global.secretsBackend.vault.enabled }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-tls-init-cleanup + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: tls-init-cleanup + annotations: + "helm.sh/hook": pre-delete + "helm.sh/hook-delete-policy": hook-succeeded +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/tls-init-job.yaml b/charts/hashicorp/consul/1.5.3/templates/tls-init-job.yaml new file mode 100644 index 0000000000..177472c9a4 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/tls-init-job.yaml @@ -0,0 +1,127 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} +{{- if not .Values.global.secretsBackend.vault.enabled }} +# tls-init job generate Consul cluster CA and certificates for the Consul servers +# and creates Kubernetes secrets for them. +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "consul.fullname" . }}-tls-init + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: tls-init + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "1" + "helm.sh/hook-delete-policy": hook-succeeded,before-hook-creation +spec: + template: + metadata: + name: {{ template "consul.fullname" . }}-tls-init + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: tls-init + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} + annotations: + "consul.hashicorp.com/connect-inject": "false" + "consul.hashicorp.com/mesh-inject": "false" + {{- if .Values.global.tls.annotations }} + {{- tpl .Values.global.tls.annotations . | nindent 8 }} + {{- end }} + spec: + restartPolicy: Never + serviceAccountName: {{ template "consul.fullname" . }}-tls-init + {{- if .Values.server.containerSecurityContext.tlsInit }} + securityContext: + {{- toYaml .Values.server.containerSecurityContext.tlsInit | nindent 8 }} + {{- end }} + {{- if (and .Values.global.tls.caCert.secretName .Values.global.tls.caKey.secretName) }} + volumes: + - name: consul-ca-cert + secret: + secretName: {{ .Values.global.tls.caCert.secretName }} + items: + - key: {{ default "tls.crt" .Values.global.tls.caCert.secretKey }} + path: tls.crt + - name: consul-ca-key + secret: + secretName: {{ .Values.global.tls.caKey.secretName }} + items: + - key: {{ default "tls.key" .Values.global.tls.caKey.secretKey }} + path: tls.key + {{- end }} + containers: + - name: tls-init + image: "{{ .Values.global.imageK8S }}" + {{ template "consul.imagePullPolicy" . }} + {{- if not .Values.server.containerSecurityContext.tlsInit }} + {{- include "consul.restrictedSecurityContext" . | nindent 10 }} + {{- end }} + env: + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + workingDir: /tmp + command: + - "/bin/sh" + - "-ec" + - | + # Suppress globbing so we can interpolate the $NAMESPACE environment variable + # and use * at the start of the dns name when setting -additional-dnsname. + set -o noglob + exec consul-k8s-control-plane tls-init \ + -log-level={{ default .Values.global.logLevel .Values.global.tls.logLevel }} \ + -log-json={{ .Values.global.logJSON }} \ + -domain={{ .Values.global.domain }} \ + -days=730 \ + -name-prefix={{ template "consul.fullname" . }} \ + -k8s-namespace=${NAMESPACE} \ + {{- if (and .Values.global.tls.caCert.secretName .Values.global.tls.caKey.secretName) }} + -ca=/consul/tls/ca/cert/tls.crt \ + -key=/consul/tls/ca/key/tls.key \ + {{- end }} + -additional-dnsname="{{ template "consul.fullname" . }}-server" \ + -additional-dnsname="*.{{ template "consul.fullname" . }}-server" \ + -additional-dnsname="*.{{ template "consul.fullname" . }}-server.${NAMESPACE}" \ + -additional-dnsname="{{ template "consul.fullname" . }}-server.${NAMESPACE}" \ + -additional-dnsname="*.{{ template "consul.fullname" . }}-server.${NAMESPACE}.svc" \ + -additional-dnsname="{{ template "consul.fullname" . }}-server.${NAMESPACE}.svc" \ + -additional-dnsname="*.server.{{ .Values.global.datacenter }}.{{ .Values.global.domain }}" \ + {{- range .Values.global.tls.serverAdditionalIPSANs }} + -additional-ipaddress={{ . }} \ + {{- end }} + {{- range .Values.global.tls.serverAdditionalDNSSANs }} + -additional-dnsname={{ . }} \ + {{- end }} + -dc={{ .Values.global.datacenter }} + {{- if (and .Values.global.tls.caCert.secretName .Values.global.tls.caKey.secretName) }} + volumeMounts: + - name: consul-ca-cert + mountPath: /consul/tls/ca/cert + readOnly: true + - name: consul-ca-key + mountPath: /consul/tls/ca/key + readOnly: true + {{- end }} + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/tls-init-podsecuritypolicy.yaml b/charts/hashicorp/consul/1.5.3/templates/tls-init-podsecuritypolicy.yaml new file mode 100644 index 0000000000..5d2a393955 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/tls-init-podsecuritypolicy.yaml @@ -0,0 +1,43 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (and (and .Values.global.tls.enabled .Values.global.enablePodSecurityPolicies) (not .Values.server.serverCert.secretName)) }} +{{- if not .Values.global.secretsBackend.vault.enabled }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-tls-init + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: tls-init + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + # Allow core volume types. + volumes: + - 'secret' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/tls-init-role.yaml b/charts/hashicorp/consul/1.5.3/templates/tls-init-role.yaml new file mode 100644 index 0000000000..216602ee9f --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/tls-init-role.yaml @@ -0,0 +1,38 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} +{{- if not .Values.global.secretsBackend.vault.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" . }}-tls-init + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: tls-init + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation +rules: +- apiGroups: [""] + resources: + - secrets + verbs: + - create + - update + - get + - list +{{- if .Values.global.enablePodSecurityPolicies }} +- apiGroups: ["policy"] + resources: + - podsecuritypolicies + verbs: + - use + resourceNames: + - {{ template "consul.fullname" . }}-tls-init +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/tls-init-rolebinding.yaml b/charts/hashicorp/consul/1.5.3/templates/tls-init-rolebinding.yaml new file mode 100644 index 0000000000..9b68d97d8c --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/tls-init-rolebinding.yaml @@ -0,0 +1,27 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} +{{- if not .Values.global.secretsBackend.vault.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" . }}-tls-init + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: tls-init + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" . }}-tls-init +subjects: +- kind: ServiceAccount + name: {{ template "consul.fullname" . }}-tls-init +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/tls-init-serviceaccount.yaml b/charts/hashicorp/consul/1.5.3/templates/tls-init-serviceaccount.yaml new file mode 100644 index 0000000000..f8504da94c --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/tls-init-serviceaccount.yaml @@ -0,0 +1,26 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} +{{- if not .Values.global.secretsBackend.vault.enabled }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-tls-init + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: tls-init + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/ui-ingress.yaml b/charts/hashicorp/consul/1.5.3/templates/ui-ingress.yaml new file mode 100644 index 0000000000..f8c7f92a77 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/ui-ingress.yaml @@ -0,0 +1,85 @@ +{{- if (and (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) (or (and (ne (.Values.ui.enabled | toString) "-") .Values.ui.enabled) (and (eq (.Values.ui.enabled | toString) "-") .Values.global.enabled)) (or (and (ne (.Values.ui.service.enabled | toString) "-") .Values.ui.service.enabled) (and (eq (.Values.ui.service.enabled | toString) "-") .Values.global.enabled))) }} +{{- if (and (ne (.Values.ui.ingress.enabled | toString) "-") .Values.ui.ingress.enabled) }} +{{- $serviceName := printf "%s-%s" (include "consul.fullname" .) "ui" -}} +{{- /* We use the kube version to determine if the apiVersion of networking.k8s.io should be v1 or v1beta1. +This is because while networks.k8s.io/v1 was introduced in Kubernetes v1.15+, the Ingress resource was +promoted to v1 only in Kubernetes v1.19+. This ensures the correct API version is chosen that supports +the Ingress resource. */}} +{{- if or ( gt .Capabilities.KubeVersion.Major "1" ) ( ge .Capabilities.KubeVersion.Minor "19" ) }} +apiVersion: networking.k8s.io/v1 +{{- else }} +apiVersion: networking.k8s.io/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ template "consul.fullname" . }}-ui + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: ui + {{- if .Values.ui.ingress.annotations }} + annotations: + {{ tpl .Values.ui.ingress.annotations . | nindent 4 | trim }} + {{- end }} +spec: + {{- if ne .Values.ui.ingress.ingressClassName "" }} + ingressClassName: {{ .Values.ui.ingress.ingressClassName }} + {{- end }} + rules: + {{- $global := .Values.global }} + {{- if or ( gt .Capabilities.KubeVersion.Major "1" ) ( ge .Capabilities.KubeVersion.Minor "19" ) }} + {{- range .Values.ui.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range (.paths | default (list "/")) }} + {{- if (or (not $global.tls.enabled) (not $global.tls.httpsOnly)) }} + - backend: + service: + name: {{ $serviceName }} + port: + number: 80 + path: {{ . }} + pathType: {{ $.Values.ui.ingress.pathType }} + {{- end }} + {{- if $global.tls.enabled }} + - backend: + service: + name: {{ $serviceName }} + port: + number: 443 + path: {{ . }} + pathType: {{ $.Values.ui.ingress.pathType }} + {{- end }} + {{- end }} + {{- end }} + {{- else }} + {{- range .Values.ui.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range (.paths | default (list "/")) }} + {{- if (or (not $global.tls.enabled) (not $global.tls.httpsOnly)) }} + - backend: + serviceName: {{ $serviceName }} + servicePort: 80 + path: {{ . }} + {{- end }} + {{- if $global.tls.enabled }} + - backend: + serviceName: {{ $serviceName }} + servicePort: 443 + path: {{ . }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- if .Values.ui.ingress.tls }} + tls: + {{- toYaml .Values.ui.ingress.tls | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/ui-service.yaml b/charts/hashicorp/consul/1.5.3/templates/ui-service.yaml new file mode 100644 index 0000000000..dc2abf4fc5 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/ui-service.yaml @@ -0,0 +1,46 @@ +{{- if (and (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) (or (and (ne (.Values.ui.enabled | toString) "-") .Values.ui.enabled) (and (eq (.Values.ui.enabled | toString) "-") .Values.global.enabled)) (or (and (ne (.Values.ui.service.enabled | toString) "-") .Values.ui.service.enabled) (and (eq (.Values.ui.service.enabled | toString) "-") .Values.global.enabled))) }} +# UI Service for Consul Server +apiVersion: v1 +kind: Service +metadata: + name: {{ template "consul.fullname" . }}-ui + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: ui + {{- if .Values.ui.service.annotations }} + annotations: + {{ tpl .Values.ui.service.annotations . | nindent 4 | trim }} + {{- end }} +spec: + selector: + app: {{ template "consul.name" . }} + release: "{{ .Release.Name }}" + component: server + ports: + {{- if (or (not .Values.global.tls.enabled) (not .Values.global.tls.httpsOnly)) }} + - name: http + port: {{ .Values.ui.service.port.http }} + targetPort: 8500 + {{- if .Values.ui.service.type }}{{ if (and (eq .Values.ui.service.type "NodePort") .Values.ui.service.nodePort.http) }} + nodePort: {{ .Values.ui.service.nodePort.http }} + {{- end }}{{ end }} + {{- end }} + {{- if .Values.global.tls.enabled }} + - name: https + port: {{ .Values.ui.service.port.https }} + targetPort: 8501 + {{- if .Values.ui.service.type }}{{ if (and (eq .Values.ui.service.type "NodePort") .Values.ui.service.nodePort.https) }} + nodePort: {{ .Values.ui.service.nodePort.https }} + {{- end }}{{ end }} + {{- end }} + {{- if .Values.ui.service.type }} + type: {{ .Values.ui.service.type }} + {{- end }} + {{- if .Values.ui.service.additionalSpec }} + {{ tpl .Values.ui.service.additionalSpec . | nindent 2 | trim }} + {{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/webhook-cert-manager-clusterrole.yaml b/charts/hashicorp/consul/1.5.3/templates/webhook-cert-manager-clusterrole.yaml new file mode 100644 index 0000000000..2a5c80d94c --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/webhook-cert-manager-clusterrole.yaml @@ -0,0 +1,54 @@ +{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName) -}} +{{- if (and .Values.connectInject.enabled (not $hasConfiguredWebhookCertsUsingVault)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "consul.fullname" . }}-webhook-cert-manager + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: webhook-cert-manager +rules: +- apiGroups: + - "" + resources: + - secrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + - validatingwebhookconfigurations + verbs: + - get + - list + - watch + - patch +- apiGroups: + - apps + resources: + - deployments + resourceNames: + - {{ template "consul.fullname" . }}-webhook-cert-manager + verbs: + - get +{{- if .Values.global.enablePodSecurityPolicies }} +- apiGroups: + - policy + resources: + - podsecuritypolicies + resourceNames: + - {{ template "consul.fullname" . }}-webhook-cert-manager + verbs: + - use +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/webhook-cert-manager-clusterrolebinding.yaml b/charts/hashicorp/consul/1.5.3/templates/webhook-cert-manager-clusterrolebinding.yaml new file mode 100644 index 0000000000..472ef4ee1d --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/webhook-cert-manager-clusterrolebinding.yaml @@ -0,0 +1,21 @@ +{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName) -}} +{{- if (and .Values.connectInject.enabled (not $hasConfiguredWebhookCertsUsingVault)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "consul.fullname" . }}-webhook-cert-manager + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: webhook-cert-manager +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "consul.fullname" . }}-webhook-cert-manager +subjects: +- kind: ServiceAccount + name: {{ template "consul.fullname" . }}-webhook-cert-manager + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/webhook-cert-manager-configmap.yaml b/charts/hashicorp/consul/1.5.3/templates/webhook-cert-manager-configmap.yaml new file mode 100644 index 0000000000..293dd32d9f --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/webhook-cert-manager-configmap.yaml @@ -0,0 +1,29 @@ +{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName) -}} +{{- if (and .Values.connectInject.enabled (not $hasConfiguredWebhookCertsUsingVault)) }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "consul.fullname" . }}-webhook-cert-manager-config + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: webhook-cert-manager +data: + webhook-config.json: |- + [ + { + "name": "{{ template "consul.fullname" . }}-connect-injector", + "tlsAutoHosts": [ + "{{ template "consul.fullname" . }}-connect-injector", + "{{ template "consul.fullname" . }}-connect-injector.{{ .Release.Namespace }}", + "{{ template "consul.fullname" . }}-connect-injector.{{ .Release.Namespace }}.svc", + "{{ template "consul.fullname" . }}-connect-injector.{{ .Release.Namespace }}.svc.cluster.local" + ], + "secretName": "{{ template "consul.fullname" . }}-connect-inject-webhook-cert", + "secretNamespace": "{{ .Release.Namespace }}" + } + ] + {{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/webhook-cert-manager-deployment.yaml b/charts/hashicorp/consul/1.5.3/templates/webhook-cert-manager-deployment.yaml new file mode 100644 index 0000000000..71cddcaf84 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/webhook-cert-manager-deployment.yaml @@ -0,0 +1,79 @@ +{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName) -}} +{{- if (and .Values.connectInject.enabled (not $hasConfiguredWebhookCertsUsingVault)) }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "consul.fullname" . }}-webhook-cert-manager + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: webhook-cert-manager + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} +spec: + replicas: 1 + selector: + matchLabels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: webhook-cert-manager + template: + metadata: + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: webhook-cert-manager + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} + annotations: + "consul.hashicorp.com/connect-inject": "false" + "consul.hashicorp.com/mesh-inject": "false" + "consul.hashicorp.com/config-checksum": {{ include (print $.Template.BasePath "/webhook-cert-manager-configmap.yaml") . | sha256sum }} + spec: + containers: + - command: + - "/bin/sh" + - "-ec" + - | + exec consul-k8s-control-plane webhook-cert-manager \ + -log-level={{ .Values.global.logLevel }} \ + -log-json={{ .Values.global.logJSON }} \ + -config-file=/bootstrap/config/webhook-config.json \ + -deployment-name={{ template "consul.fullname" . }}-webhook-cert-manager \ + -deployment-namespace={{ .Release.Namespace }} + image: {{ .Values.global.imageK8S }} + {{ template "consul.imagePullPolicy" . }} + name: webhook-cert-manager + {{- include "consul.restrictedSecurityContext" . | nindent 8 }} + {{- with .Values.webhookCertManager.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + - name: config + mountPath: /bootstrap/config + terminationGracePeriodSeconds: 10 + serviceAccountName: {{ template "consul.fullname" . }}-webhook-cert-manager + volumes: + - name: config + configMap: + name: {{ template "consul.fullname" . }}-webhook-cert-manager-config + {{- if .Values.webhookCertManager.tolerations }} + tolerations: + {{ tpl .Values.webhookCertManager.tolerations . | indent 8 | trim }} + {{- end }} + {{- if .Values.webhookCertManager.nodeSelector }} + nodeSelector: + {{ tpl .Values.webhookCertManager.nodeSelector . | indent 8 | trim }} + {{- end }} + +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/webhook-cert-manager-podsecuritypolicy.yaml b/charts/hashicorp/consul/1.5.3/templates/webhook-cert-manager-podsecuritypolicy.yaml new file mode 100644 index 0000000000..4d685edc39 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/webhook-cert-manager-podsecuritypolicy.yaml @@ -0,0 +1,43 @@ +{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName) -}} +{{- if (and .Values.global.enablePodSecurityPolicies (or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled))) }} +{{- if (and .Values.connectInject.enabled (not $hasConfiguredWebhookCertsUsingVault)) }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-webhook-cert-manager + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: webhook-cert-manager +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + # Allow core volume types. + volumes: + - 'configMap' + - 'emptyDir' + - 'projected' + - 'secret' + - 'downwardAPI' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/templates/webhook-cert-manager-serviceaccount.yaml b/charts/hashicorp/consul/1.5.3/templates/webhook-cert-manager-serviceaccount.yaml new file mode 100644 index 0000000000..68c54f3c27 --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/templates/webhook-cert-manager-serviceaccount.yaml @@ -0,0 +1,20 @@ +{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName) -}} +{{- if (and .Values.connectInject.enabled (not $hasConfiguredWebhookCertsUsingVault)) }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-webhook-cert-manager + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: webhook-cert-manager + {{- with .Values.global.imagePullSecrets }} +imagePullSecrets: + {{- range . }} +- name: {{ .name }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/1.5.3/values.yaml b/charts/hashicorp/consul/1.5.3/values.yaml new file mode 100644 index 0000000000..51ce06d61e --- /dev/null +++ b/charts/hashicorp/consul/1.5.3/values.yaml @@ -0,0 +1,3669 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +# Available parameters and their default values for the Consul chart. + +# Holds values that affect multiple components of the chart. +global: + # The main enabled/disabled setting. If true, servers, + # clients, Consul DNS and the Consul UI will be enabled. Each component can override + # this default via its component-specific "enabled" config. If false, no components + # will be installed by default and per-component opt-in is required, such as by + # setting `server.enabled` to true. + enabled: true + + # The default log level to apply to all components which do not otherwise override this setting. + # It is recommended to generally not set this below "info" unless actively debugging due to logging verbosity. + # One of "debug", "info", "warn", or "error". + # @type: string + logLevel: "info" + + # Enable all component logs to be output in JSON format. + # @type: boolean + logJSON: false + + # Set the prefix used for all resources in the Helm chart. If not set, + # the prefix will be `-consul`. + # @type: string + name: null + + # The domain Consul will answer DNS queries for + # (Refer to [`-domain`](https://developer.hashicorp.com/consul/docs/agent/config/cli-flags#_domain)) and the domain services synced from + # Consul into Kubernetes will have, e.g. `service-name.service.consul`. + domain: consul + + # Configures the Cluster Peering feature. Requires Consul v1.14+ and Consul-K8s v1.0.0+. + peering: + # If true, the Helm chart enables Cluster Peering for the cluster. This option enables peering controllers and + # allows use of the PeeringAcceptor and PeeringDialer CRDs for establishing service mesh peerings. + enabled: false + + # [Enterprise Only] Enabling `adminPartitions` allows creation of Admin Partitions in Kubernetes clusters. + # It additionally indicates that you are running Consul Enterprise v1.11+ with a valid Consul Enterprise + # license. Admin partitions enables deploying services across partitions, while sharing + # a set of Consul servers. + adminPartitions: + # If true, the Helm chart will enable Admin Partitions for the cluster. The clients in the server cluster + # must be installed in the default partition. Creation of Admin Partitions is only supported during installation. + # Admin Partitions cannot be installed via a Helm upgrade operation. Only Helm installs are supported. + enabled: false + + # The name of the Admin Partition. The partition name cannot be modified once the partition has been installed. + # Changing the partition name would require an un-install and a re-install with the updated name. + # Must be "default" in the server cluster ie the Kubernetes cluster that the Consul server pods are deployed onto. + name: "default" + + # The name (and tag) of the Consul Docker image for clients and servers. + # This can be overridden per component. This should be pinned to a specific + # version tag, otherwise you may inadvertently upgrade your Consul version. + # + # Examples: + # + # ```yaml + # # Consul 1.10.0 + # image: "consul:1.10.0" + # # Consul Enterprise 1.10.0 + # image: "hashicorp/consul-enterprise:1.10.0-ent" + # ``` + # @default: hashicorp/consul: + image: hashicorp/consul:1.19.2 + + # Array of objects containing image pull secret names that will be applied to each service account. + # This can be used to reference image pull secrets if using a custom consul or consul-k8s-control-plane Docker image. + # Refer to https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry. + # + # Example: + # + # ```yaml + # imagePullSecrets: + # - name: pull-secret-name + # - name: pull-secret-name-2 + # ``` + # @type: array + imagePullSecrets: [] + + # The name (and tag) of the consul-k8s-control-plane Docker + # image that is used for functionality such as catalog sync. + # This can be overridden per component. + # @default: hashicorp/consul-k8s-control-plane: + imageK8S: hashicorp/consul-k8s-control-plane:1.5.3 + + # The image pull policy used globally for images controlled by Consul (consul, consul-dataplane, consul-k8s, consul-telemetry-collector). + # One of "IfNotPresent", "Always", "Never", and "". Refer to https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy + # @default: "" + imagePullPolicy: "" + + # The name of the datacenter that the agents should + # register as. This can't be changed once the Consul cluster is up and running + # since Consul doesn't support an automatic way to change this value currently: + # https://github.com/hashicorp/consul/issues/1858. + datacenter: dc1 + + # Controls whether pod security policies are created for the Consul components + # created by this chart. Refer to https://kubernetes.io/docs/concepts/policy/pod-security-policy/. + enablePodSecurityPolicies: false + + # secretsBackend is used to configure Vault as the secrets backend for the Consul on Kubernetes installation. + # The Vault cluster needs to have the Kubernetes Auth Method, KV2 and PKI secrets engines enabled + # and have necessary secrets, policies and roles created prior to installing Consul. + # Refer to [Vault as the Secrets Backend](https://developer.hashicorp.com/consul/docs/k8s/deployment-configurations/vault) + # documentation for full instructions. + # + # The Vault cluster _must_ not have the Consul cluster installed by this Helm chart as its storage backend + # as that would cause a circular dependency. + # Vault can have Consul as its storage backend as long as that Consul cluster is not running on this Kubernetes cluster + # and is being managed separately from this Helm installation. + # + # Note: When using Vault KV2 secrets engines the "data" field is implicitly required for Vault API calls, + # secretName should be in the form of "vault-kv2-mount-path/data/secret-name". + # secretKey should be in the form of "key". + secretsBackend: + vault: + # Vault namespace (optional). This sets the Vault namespace for the `vault.hashicorp.com/namespace` + # agent annotation and [Vault Connect CA namespace](https://developer.hashicorp.com/consul/docs/connect/ca/vault#namespace). + # To override one of these values individually, see `agentAnnotations` and `connectCA.additionalConfig`. + vaultNamespace: "" + + # Enabling the Vault secrets backend will replace Kubernetes secrets with referenced Vault secrets. + enabled: false + + # The Vault role for the Consul server. + # The role must be connected to the Consul server's service account. + # The role must also have a policy with read capabilities for the following secrets: + # - gossip encryption key defined by the `global.gossipEncryption.secretName` value + # - certificate issue path defined by the `server.serverCert.secretName` value + # - CA certificate defined by the `global.tls.caCert.secretName` value + # - replication token defined by the `global.acls.replicationToken.secretName` value if `global.federation.enabled` is `true` + # To discover the service account name of the Consul server, run + # ```shell-session + # $ helm template --show-only templates/server-serviceaccount.yaml hashicorp/consul + # ``` + # and check the name of `metadata.name`. + consulServerRole: "" + + # The Vault role for the Consul client. + # The role must be connected to the Consul client's service account. + # The role must also have a policy with read capabilities for the gossip encryption + # key defined by the `global.gossipEncryption.secretName` value. + # To discover the service account name of the Consul client, run + # ```shell-session + # $ helm template --show-only templates/client-serviceaccount.yaml hashicorp/consul + # ``` + # and check the name of `metadata.name`. + consulClientRole: "" + + # A Vault role for the Consul `server-acl-init` job, which manages setting ACLs so that clients and components can obtain ACL tokens. + # The role must be connected to the `server-acl-init` job's service account. + # The role must also have a policy with read and write capabilities for the bootstrap, replication or partition tokens + # To discover the service account name of the `server-acl-init` job, run + # ```shell-session + # $ helm template --show-only templates/server-acl-init-serviceaccount.yaml \ + # --set global.acls.manageSystemACLs=true hashicorp/consul + # ``` + # and check the name of `metadata.name`. + manageSystemACLsRole: "" + + # [Enterprise Only] A Vault role that allows the Consul `partition-init` job to read a Vault secret for the partition ACL token. + # The `partition-init` job bootstraps Admin Partitions on Consul servers. + # . + # This role must be bound the `partition-init` job's service account. + # To discover the service account name of the `partition-init` job, run with Helm values for the client cluster: + # ```shell-session + # $ helm template --show-only templates/partition-init-serviceaccount.yaml -f client-cluster-values.yaml hashicorp/consul + # ``` + # and check the name of `metadata.name`. + adminPartitionsRole: "" + + # The Vault role to read Consul connect-injector webhook's CA + # and issue a certificate and private key. + # A Vault policy must be created which grants issue capabilities to + # `global.secretsBackend.vault.connectInject.tlsCert.secretName`. + connectInjectRole: "" + + # The Vault role for all Consul components to read the Consul's server's CA Certificate (unauthenticated). + # The role should be connected to the service accounts of all Consul components, or alternatively `*` since it + # will be used only against the `pki/cert/ca` endpoint which is unauthenticated. A policy must be created which grants + # read capabilities to `global.tls.caCert.secretName`, which is usually `pki/cert/ca`. + consulCARole: "" + + # This value defines additional annotations for + # Vault agent on any pods where it'll be running. + # This should be formatted as a multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + agentAnnotations: null + + # Configuration for Vault server CA certificate. This certificate will be mounted + # to any pod where Vault agent needs to run. + ca: + # The name of the Kubernetes or Vault secret that holds the Vault CA certificate. + # A Kubernetes secret must be in the same namespace that Consul is installed into. + secretName: "" + # The key within the Kubernetes or Vault secret that holds the Vault CA certificate. + secretKey: "" + + # Configuration for the Vault Connect CA provider. + # The provider will be configured to use the Vault Kubernetes auth method + # and therefore requires the role provided by `global.secretsBackend.vault.consulServerRole` + # to have permissions to the root and intermediate PKI paths. + # Please refer to [Vault ACL policies](https://developer.hashicorp.com/consul/docs/connect/ca/vault#vault-acl-policies) + # documentation for information on how to configure the Vault policies. + connectCA: + # The address of the Vault server. + address: "" + + # The mount path of the Kubernetes auth method in Vault. + authMethodPath: "kubernetes" + + # The path to a PKI secrets engine for the root certificate. + # For more details, please refer to [Vault Connect CA configuration](https://developer.hashicorp.com/consul/docs/connect/ca/vault#rootpkipath). + rootPKIPath: "" + + # The path to a PKI secrets engine for the generated intermediate certificate. + # For more details, please refer to [Vault Connect CA configuration](https://developer.hashicorp.com/consul/docs/connect/ca/vault#intermediatepkipath). + intermediatePKIPath: "" + + # Additional Connect CA configuration in JSON format. + # Please refer to [Vault Connect CA configuration](https://developer.hashicorp.com/consul/docs/connect/ca/vault#configuration) + # for all configuration options available for that provider. + # + # Example: + # + # ```yaml + # additionalConfig: | + # { + # "connect": [{ + # "ca_config": [{ + # "leaf_cert_ttl": "36h" + # }] + # }] + # } + # ``` + additionalConfig: | + {} + + connectInject: + # Configuration to the Vault Secret that Kubernetes uses on + # Kubernetes pod creation, deletion, and update, to get CA certificates + # used issued from vault to send webhooks to the ConnectInject. + caCert: + # The Vault secret path that contains the CA certificate for + # Connect Inject webhooks. + # @type: string + secretName: null + + # Configuration to the Vault Secret that Kubernetes uses on + # Kubernetes pod creation, deletion, and update, to get TLS certificates + # used issued from vault to send webhooks to the ConnectInject. + tlsCert: + # The Vault secret path that issues TLS certificates for connect + # inject webhooks. + # @type: string + secretName: null + + # Configures Consul's gossip encryption key. + # (Refer to [`-encrypt`](https://developer.hashicorp.com/consul/docs/agent/config/cli-flags#_encrypt)). + # By default, gossip encryption is not enabled. The gossip encryption key may be set automatically or manually. + # The recommended method is to automatically generate the key. + # To automatically generate and set a gossip encryption key, set autoGenerate to true. + # Values for secretName and secretKey should not be set if autoGenerate is true. + # To manually generate a gossip encryption key, set secretName and secretKey and use Consul to generate + # a key, saving this as a Kubernetes secret or Vault secret path and key. + # If `global.secretsBackend.vault.enabled=true`, be sure to add the "data" component of the secretName path as required by + # the Vault KV-2 secrets engine [refer to example]. + # + # ```shell-session + # $ kubectl create secret generic consul-gossip-encryption-key --from-literal=key=$(consul keygen) + # ``` + # + # Vault CLI Example: + # ```shell-session + # $ vault kv put consul/secrets/gossip key=$(consul keygen) + # ``` + # `gossipEncryption.secretName="consul/data/secrets/gossip"` + # `gossipEncryption.secretKey="key"` + + gossipEncryption: + # Automatically generate a gossip encryption key and save it to a Kubernetes or Vault secret. + autoGenerate: false + # The name of the Kubernetes secret or Vault secret path that holds the gossip + # encryption key. A Kubernetes secret must be in the same namespace that Consul is installed into. + secretName: "" + # The key within the Kubernetes secret or Vault secret key that holds the gossip + # encryption key. + secretKey: "" + # Override global log verbosity level for gossip-encryption-autogenerate-job pods. One of "trace", "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + + # A list of addresses of upstream DNS servers that are used to recursively resolve DNS queries. + # These values are given as `-recursor` flags to Consul servers and clients. + # Refer to [`-recursor`](https://developer.hashicorp.com/consul/docs/agent/config/cli-flags#_recursor) for more details. + # If this is an empty array (the default), then Consul DNS will only resolve queries for the Consul top level domain (by default `.consul`). + # @type: array + recursors: [] + + # Enables [TLS](https://developer.hashicorp.com/consul/tutorials/security/tls-encryption-secure) + # across the cluster to verify authenticity of the Consul servers and clients. + # Requires Consul v1.4.1+. + tls: + # If true, the Helm chart will enable TLS for Consul + # servers and clients and all consul-k8s-control-plane components, as well as generate certificate + # authority (optional) and server and client certificates. + # This setting is required for [Cluster Peering](https://developer.hashicorp.com/consul/docs/connect/cluster-peering/k8s). + enabled: false + + # Override global log verbosity level. One of "trace", "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + + # If true, turns on the auto-encrypt feature on clients and servers. + # It also switches consul-k8s-control-plane components to retrieve the CA from the servers + # via the API. Requires Consul 1.7.1+. + enableAutoEncrypt: false + + # A list of additional DNS names to set as Subject Alternative Names (SANs) + # in the server certificate. This is useful when you need to access the + # Consul server(s) externally, for example, if you're using the UI. + # @type: array + serverAdditionalDNSSANs: [] + + # A list of additional IP addresses to set as Subject Alternative Names (SANs) + # in the server certificate. This is useful when you need to access the + # Consul server(s) externally, for example, if you're using the UI. + # @type: array + serverAdditionalIPSANs: [] + + # If true, `verify_outgoing`, `verify_server_hostname`, + # and `verify_incoming` for internal RPC communication will be set to `true` for Consul servers and clients. + # Set this to false to incrementally roll out TLS on an existing Consul cluster. + # Please refer to [TLS on existing clusters](https://developer.hashicorp.com/consul/docs/k8s/operations/tls-on-existing-cluster) + # for more details. + verify: true + + # If true, the Helm chart will configure Consul to disable the HTTP port on + # both clients and servers and to only accept HTTPS connections. + httpsOnly: true + + # A secret containing the certificate of the CA to use for TLS communication within the Consul cluster. + # If you have generated the CA yourself with the consul CLI, you could use the following command to create the secret + # in Kubernetes: + # + # ```shell-session + # $ kubectl create secret generic consul-ca-cert \ + # --from-file='tls.crt=./consul-agent-ca.pem' + # ``` + # If you are using Vault as a secrets backend with TLS, `caCert.secretName` must be provided and should reference + # the CA path for your PKI secrets engine. This should be of the form `pki/cert/ca` where `pki` is the mount point of your PKI secrets engine. + # A read policy must be created and associated with the CA cert path for `global.tls.caCert.secretName`. + # This will be consumed by the `global.secretsBackend.vault.consulCARole` role by all Consul components. + # When using Vault the secretKey is not used. + caCert: + # The name of the Kubernetes or Vault secret that holds the CA certificate. + # @type: string + secretName: null + # The key within the Kubernetes or Vault secret that holds the CA certificate. + # @type: string + secretKey: null + + # A Kubernetes or Vault secret containing the private key of the CA to use for + # TLS communication within the Consul cluster. If you have generated the CA yourself + # with the consul CLI, you could use the following command to create the secret + # in Kubernetes: + # + # ```shell-session + # $ kubectl create secret generic consul-ca-key \ + # --from-file='tls.key=./consul-agent-ca-key.pem' + # ``` + # + # Note that we need the CA key so that we can generate server and client certificates. + # It is particularly important for the client certificates since they need to have host IPs + # as Subject Alternative Names. If you are setting server certs yourself via `server.serverCert` + # and you are not enabling clients (or clients are enabled with autoEncrypt) then you do not + # need to provide the CA key. + caKey: + # The name of the Kubernetes or Vault secret that holds the CA key. + # @type: string + secretName: null + # The key within the Kubernetes or Vault secret that holds the CA key. + # @type: string + secretKey: null + + # This value defines additional annotations for + # tls init jobs. This should be formatted as a multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + # [Enterprise Only] `enableConsulNamespaces` indicates that you are running + # Consul Enterprise v1.7+ with a valid Consul Enterprise license and would + # like to make use of configuration beyond registering everything into + # the `default` Consul namespace. Additional configuration + # options are found in the `consulNamespaces` section of both the catalog sync + # and connect injector. + enableConsulNamespaces: false + + # Configure ACLs. + acls: + # If true, the Helm chart will automatically manage ACL tokens and policies + # for all Consul and consul-k8s-control-plane components. + # This requires Consul >= 1.4. + manageSystemACLs: false + + # Override global log verbosity level. One of "trace", "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + + # A Kubernetes or Vault secret containing the bootstrap token to use for creating policies and + # tokens for all Consul and consul-k8s-control-plane components. If `secretName` and `secretKey` + # are unset, a default secret name and secret key are used. If the secret is populated, then + # we will skip ACL bootstrapping of the servers and will only initialize ACLs for the Consul + # clients and consul-k8s-control-plane system components. + # If the secret is empty, then we will bootstrap ACLs on the Consul servers, and write the + # bootstrap token to this secret. If ACLs are already bootstrapped on the servers, then the + # secret must contain the bootstrap token. + bootstrapToken: + # The name of the Kubernetes or Vault secret that holds the bootstrap token. + # If unset, this defaults to `{{ global.name }}-bootstrap-acl-token`. + secretName: null + # The key within the Kubernetes or Vault secret that holds the bootstrap token. + # If unset, this defaults to `token`. + secretKey: null + + # If true, an ACL token will be created that can be used in secondary + # datacenters for replication. This should only be set to true in the + # primary datacenter since the replication token must be created from that + # datacenter. + # In secondary datacenters, the secret needs to be imported from the primary + # datacenter and referenced via `global.acls.replicationToken`. + createReplicationToken: false + + # replicationToken references a secret containing the replication ACL token. + # This token will be used by secondary datacenters to perform ACL replication + # and create ACL tokens and policies. + # This value is ignored if `bootstrapToken` is also set. + replicationToken: + # The name of the Kubernetes or Vault secret that holds the replication token. + # @type: string + secretName: null + # The key within the Kubernetes or Vault secret that holds the replication token. + # @type: string + secretKey: null + + # The resource requests (CPU, memory, etc.) for the server-acl-init and server-acl-init-cleanup pods. + # This should be a YAML map corresponding to a Kubernetes + # [`ResourceRequirements``](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#resourcerequirements-v1-core) + # object. + # + # Example: + # + # ```yaml + # resources: + # requests: + # memory: '200Mi' + # cpu: '100m' + # limits: + # memory: '200Mi' + # cpu: '100m' + # ``` + # + # @recurse: false + # @type: map + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" + + # partitionToken references a Vault secret containing the ACL token to be used in non-default partitions. + # This value should only be provided in the default partition and only when setting + # the `global.secretsBackend.vault.enabled` value to true. + # Consul will use the value of the secret stored in Vault to create an ACL token in Consul with the value of the + # secret as the secretID for the token. + # In non-default, partitions set this secret as the `bootstrapToken`. + partitionToken: + # The name of the Vault secret that holds the partition token. + # @type: string + secretName: null + # The key within the Vault secret that holds the parition token. + # @type: string + secretKey: null + + # tolerations configures the taints and tolerations for the server-acl-init + # and server-acl-init-cleanup jobs. This should be a multi-line string matching the + # [Tolerations](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) array in a Pod spec. + tolerations: "" + + # This value defines [`nodeSelector`](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) + # labels for the server-acl-init and server-acl-init-cleanup jobs pod assignment, formatted as a multi-line string. + # + # Example: + # + # ```yaml + # nodeSelector: | + # beta.kubernetes.io/arch: amd64 + # ``` + # + # @type: string + nodeSelector: null + + # This value defines additional annotations for + # acl init jobs. This should be formatted as a multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + # If argocd.enabled is set to true, following annotations are added to + # job - server-acl-init-job + # annotations - + # argocd.argoproj.io/hook: Sync + # argocd.argoproj.io/hook-delete-policy: HookSucceeded + argocd: + enabled: false + + # [Enterprise Only] This value refers to a Kubernetes or Vault secret that you have created + # that contains your enterprise license. It is required if you are using an + # enterprise binary. Defining it here applies it to your cluster once a leader + # has been elected. If you are not using an enterprise image or if you plan to + # introduce the license key via another route, then set these fields to null. + # Note: the job to apply license runs on both Helm installs and upgrades. + enterpriseLicense: + # The name of the Kubernetes or Vault secret that holds the enterprise license. + # A Kubernetes secret must be in the same namespace that Consul is installed into. + # @type: string + secretName: null + # The key within the Kubernetes or Vault secret that holds the enterprise license. + # @type: string + secretKey: null + # Manages license autoload. Required in Consul 1.10.0+, 1.9.7+ and 1.8.12+. + enableLicenseAutoload: true + + # Configure federation. + federation: + # If enabled, this datacenter will be federation-capable. Only federation + # via mesh gateways is supported. + # Mesh gateways and servers will be configured to allow federation. + # Requires `global.tls.enabled`, `connectInject.enabled`, and one of + # `meshGateway.enabled` or `externalServers.enabled` to be true. + # Requires Consul 1.8+. + enabled: false + + # If true, the chart will create a Kubernetes secret that can be imported + # into secondary datacenters so they can federate with this datacenter. The + # secret contains all the information secondary datacenters need to contact + # and authenticate with this datacenter. This should only be set to true + # in your primary datacenter. The secret name is + # `-federation` (if setting `global.name`), otherwise + # `-consul-federation`. + createFederationSecret: false + + # The name of the primary datacenter. + # @type: string + primaryDatacenter: null + + # A list of addresses of the primary mesh gateways in the form `:` + # (e.g. `["1.1.1.1:443", "2.3.4.5:443"]`). + # @type: array + primaryGateways: [] + + # If you are setting `global.federation.enabled` to true and are in a secondary datacenter, + # set `k8sAuthMethodHost` to the address of the Kubernetes API server of the secondary datacenter. + # This address must be reachable from the Consul servers in the primary datacenter. + # This auth method will be used to provision ACL tokens for Consul components and is different + # from the one used by the Consul Service Mesh. + # Please refer to the [Kubernetes Auth Method documentation](https://developer.hashicorp.com/consul/docs/security/acl/auth-methods/kubernetes). + # + # If `externalServers.enabled` is set to true, `global.federation.k8sAuthMethodHost` and + # `externalServers.k8sAuthMethodHost` should be set to the same value. + # + # You can retrieve this value from your `kubeconfig` by running: + # + # ```shell-session + # $ kubectl config view \ + # -o jsonpath="{.clusters[?(@.name=='')].cluster.server}" + # ``` + # + # @type: string + k8sAuthMethodHost: null + + # Override global log verbosity level for the create-federation-secret-job pods. One of "trace", "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + + # Configures metrics for Consul service mesh + metrics: + # Configures the Helm chart’s components + # to expose Prometheus metrics for the Consul service mesh. By default + # this includes gateway metrics and sidecar metrics. + # @type: boolean + enabled: false + + # Configures consul agent metrics. Only applicable if + # `global.metrics.enabled` is true. + # @type: boolean + enableAgentMetrics: false + + # Set to true to stop prepending the machine's hostname to gauge-type metrics. Default is false. + # Only applicable if `global.metrics.enabled` and `global.metrics.enableAgentMetrics` is true. + # @type: boolean + disableAgentHostName: false + + # Configures consul agent underlying host metrics. Default is false. + # Only applicable if `global.metrics.enabled` and `global.metrics.enableAgentMetrics` is true. + # @type: boolean + enableHostMetrics: false + + # Configures the retention time for metrics in Consul clients and + # servers. This must be greater than 0 for Consul clients and servers + # to expose any metrics at all. + # Only applicable if `global.metrics.enabled` is true. + # @type: string + agentMetricsRetentionTime: 1m + + # If true, mesh, terminating, and ingress gateways will expose their + # Envoy metrics on port `20200` at the `/metrics` path and all gateway pods + # will have Prometheus scrape annotations. Only applicable if `global.metrics.enabled` is true. + # @type: boolean + enableGatewayMetrics: true + + # Configures the Helm chart’s components to forward envoy metrics for the Consul service mesh to the + # consul-telemetry-collector. This includes gateway metrics and sidecar metrics. + # @type: boolean + enableTelemetryCollector: false + + # Configures the list of filter rules to apply for allowing or blocking + # metrics by prefix in the following format: + # + # A leading "+" will enable any metrics with the given prefix, and a leading "-" will block them. + # If there is overlap between two rules, the more specific rule will take precedence. + # Blocking will take priority if the same prefix is listed multiple times. + prefixFilter: + # @type: array + allowList: [] + # @type: array + blockList: [] + + # Configures consul integration configurations for datadog on kubernetes. + # Only applicable if `global.metrics.enabled` and `global.metrics.enableAgentMetrics` is true. + datadog: + # Enables datadog [Consul Autodiscovery Integration](https://docs.datadoghq.com/integrations/consul/?tab=containerized#metric-collection) + # by configuring the required `ad.datadoghq.com/consul.checks` annotation. The following _Consul_ agent metrics/health statuses + # are monitored by Datadog unless monitoring via OpenMetrics (Prometheus) or DogStatsD: + # - Serf events and member flaps + # - The Raft protocol + # - DNS performance + # - API Endpoints scraped: + # - `/v1/agent/metrics?format=prometheus` + # - `/v1/agent/self` + # - `/v1/status/leader` + # - `/v1/status/peers` + # - `/v1/catalog/services` + # - `/v1/health/service` + # - `/v1/health/state/any` + # - `/v1/coordinate/datacenters` + # - `/v1/coordinate/nodes` + # + # Setting either `global.metrics.datadog.otlp.enabled=true` or `global.metrics.datadog.dogstatsd.enabled=true` disables the above checks + # in lieu of metrics data collection via DogStatsD or by a customer OpenMetrics (Prometheus) collection endpoint. + # + # ~> **Note:** If you have a [dogstatsd_mapper_profile](https://docs.datadoghq.com/integrations/consul/?tab=host#dogstatsd) configured for Consul + # residing on either your Datadog NodeAgent or ClusterAgent the default Consul agent metrics/health status checks will fail. If you do not desire + # to utilize DogStatsD metrics emission from Consul, remove this configuration file, and restart your Datadog agent to permit the checks to run. + # + # @default: false + # @type: boolean + enabled: false + + # Configures Kubernetes Prometheus/OpenMetrics auto-discovery annotations for use with Datadog. + # This configuration is less common and more for advanced usage with custom metrics monitoring + # configurations. Refer to the [Datadog documentation](https://docs.datadoghq.com/containers/kubernetes/prometheus/?tab=kubernetesadv2) for more details. + openMetricsPrometheus: + # @default: false + # @type: boolean + enabled: false + + otlp: + # Enables forwarding of Consul's Telemetry Collector OTLP metrics for + # ingestion by Datadog Agent. + # @default: false + # @type: boolean + enabled: false + # Protocol used for DataDog Endpoint OTLP ingestion. + # + # Valid protocol options are one of either: + # + # - "http": will forward to DataDog HTTP OTLP Node Agent Endpoint default - "0.0.0.0:4318" + # - "grpc": will forward to DataDog gRPC OTLP Node Agent Endpoint default - "0.0.0.0:4317" + # + # @default: "http" + # @type: string + protocol: "http" + + # Configuration settings for DogStatsD metrics aggregation service + # that is bundled with the Datadog Agent. + # DogStatsD implements the StatsD protocol and adds a few Datadog-specific extensions: + # - Histogram metric type + # - Service checks + # - Events + # - Tagging + dogstatsd: + enabled: false + # Sets the socket transport type for dogstatsd: + # - "UDS" (Unix Domain Socket): prefixes `unix://` to URL and appends path to socket (i.e., "unix:///var/run/datadog/dsd.socket") + # If set, this will create the required [hostPath](https://kubernetes.io/docs/concepts/storage/volumes/#hostpath) mount for + # managing [DogStatsD with Unix Domain Socket on Kubernetes](https://docs.datadoghq.com/developers/dogstatsd/unix_socket/?tab=kubernetes). + # The volume is mounted using the `DirectoryOrCreate` type, thereby setting `0755` permissions with the same kubelet group ownership. + # + # Applies the following `volumes` and `volumeMounts` to the consul-server stateful set consul containers: + # + # ```yaml + # volumes: + # - name: dsdsocket + # hostPath: + # path: /var/run/datadog + # type: DirectoryOrCreate + # volumeMounts: + # - name: dsdsocket + # mountPath: /var/run/datadog + # readOnly: true + # ``` + # - "UDP" (User Datagram Protocol): assigns address to use `hostname/IP:Port` formatted URL for UDP transport to hostIP based + # dogstatsd sink (i.e., 127.0.0.1:8125). HostIP of Datadog agent must be reachable and known to Consul server emitting metrics. + # + # @default: "UDS" + # @type: string + socketTransportType: "UDS" + # Sets URL path for dogstatsd: + # + # Can be either a path to unix domain socket or an IP Address or Hostname that's reachable from the + # consul-server service, server containers. When using "UDS" the path will be appended. When using "UDP" + # the path will be prepended to the specified `dogstatsdPort`. + # + # @default: "/var/run/datadog/dsd.socket" + # @type: string + dogstatsdAddr: "/var/run/datadog/dsd.socket" + # Configures IP based dogstatsd designated port that will be appended to "UDP" based transport socket IP/Hostname URL. + # + # If using a kubernetes service based address (i.e., datadog.default.svc.cluster.local), set this to 0 to + # mitigate appending a port value to the dogstatsd address field. Resultant address would be "datadog.default.svc.cluster.local" with + # default port setting, while appending a non-zero port would result in "172.10.23.6:8125" with a dogstatsdAddr value + # of "172.10.23.6". + # + # @default: 0 + # @type: integer + dogstatsdPort: 0 + # Configures datadog [autodiscovery](https://docs.datadoghq.com/containers/kubernetes/log/?tab=operator#autodiscovery) + # style [log integration](https://docs.datadoghq.com/integrations/consul/?tab=containerized#log-collection) + # configuration for Consul. + # + # The default settings should handle most Consul Kubernetes deployment schemes. The resultant annotation + # will reside on the consul-server statefulset as autodiscovery annotations. + # (i.e., ad.datadoghq.com/consul.logs: ["source:consul","consul_service:consul-server", ""]) + # + # @default: ["source:consul","consul_service:consul-server"] + # @type: array + dogstatsdTags: ["source:consul","consul_service:consul-server"] + # Namespace + # + # @default: "default" + # @type: string + namespace: "default" + + + # The name (and tag) of the consul-dataplane Docker image used for the + # connect-injected sidecar proxies and mesh, terminating, and ingress gateways. + # @default: hashicorp/consul-dataplane: + imageConsulDataplane: hashicorp/consul-dataplane:1.5.3 + + # Configuration for running this Helm chart on the Red Hat OpenShift platform. + # This Helm chart currently supports OpenShift v4.x+. + openshift: + # If true, the Helm chart will create necessary configuration for running + # its components on OpenShift. + enabled: false + + # The time in seconds that the consul API client will wait for a response from + # the API before cancelling the request. + consulAPITimeout: 5s + + # Enables installing an HCP Consul Central self-managed cluster. + # Requires Consul v1.14+. + cloud: + # If true, the Helm chart will link a [self-managed cluster to HCP](https://developer.hashicorp.com/hcp/docs/consul/self-managed). + # This can either be used to [configure a new cluster](https://developer.hashicorp.com/hcp/docs/consul/self-managed/new) + # or [link an existing one](https://developer.hashicorp.com/hcp/docs/consul/self-managed/existing). + # + # Note: this setting should not be enabled for [HCP Consul Dedicated clusters](/hcp/docs/consul/dedicated). + # It is strictly for linking self-managed clusters. + enabled: false + + # The resource id of the HCP Consul Central cluster to link to. Eg: + # organization/27109cd4-a309-4bf3-9986-e1d071914b18/project/fcef6c24-259d-4510-bb8d-1d812e120e34/hashicorp.consul.global-network-manager.cluster/consul-cluster + # This is required when global.cloud.enabled is true. + resourceId: + # The name of the Kubernetes secret that holds the resource id. + # @type: string + secretName: null + # The key within the Kubernetes secret that holds the resource id. + # @type: string + secretKey: null + + # The client id portion of a [service principal](https://developer.hashicorp.com/hcp/docs/hcp/admin/iam/service-principals#service-principals) with authorization to link the cluster + # in global.cloud.resourceId to HCP Consul Central. + # This is required when global.cloud.enabled is true. + clientId: + # The name of the Kubernetes secret that holds the client id. + # @type: string + secretName: null + # The key within the Kubernetes secret that holds the client id. + # @type: string + secretKey: null + + # The client secret portion of a [service principal](https://developer.hashicorp.com/hcp/docs/hcp/admin/iam/service-principals#service-principals) with authorization to link the cluster + # in global.cloud.resourceId to HCP Consul Central. + # This is required when global.cloud.enabled is true. + clientSecret: + # The name of the Kubernetes secret that holds the client secret. + # @type: string + secretName: null + # The key within the Kubernetes secret that holds the client secret. + # @type: string + secretKey: null + + # The hostname of HCP's API. This setting is used for internal testing and validation. + apiHost: + # The name of the Kubernetes secret that holds the api hostname. + # @type: string + secretName: null + # The key within the Kubernetes secret that holds the api hostname. + # @type: string + secretKey: null + + # The URL of HCP's auth API. This setting is used for internal testing and validation. + authUrl: + # The name of the Kubernetes secret that holds the authorization url. + # @type: string + secretName: null + # The key within the Kubernetes secret that holds the authorization url. + # @type: string + secretKey: null + + # The address of HCP's scada service. This setting is used for internal testing and validation. + scadaAddress: + # The name of the Kubernetes secret that holds the scada address. + # @type: string + secretName: null + # The key within the Kubernetes secret that holds the scada address. + # @type: string + secretKey: null + + # Extra labels to attach to all pods, deployments, daemonsets, statefulsets, and jobs. This should be a YAML map. + # + # Example: + # + # ```yaml + # extraLabels: + # labelKey: label-value + # anotherLabelKey: another-label-value + # ``` + # + # @type: map + extraLabels: {} + + # Optional PEM-encoded CA certificates that will be added to trusted system CAs. + # + # Example: + # + # ```yaml + # trustedCAs: [ + # | + # -----BEGIN CERTIFICATE----- + # MIIC7jCCApSgAwIBAgIRAIq2zQEVexqxvtxP6J0bXAwwCgYIKoZIzj0EAwIwgbkx + # ... + # ] + # ``` + # @type: array + trustedCAs: [] + + # Consul feature flags that will be enabled across components. + # Supported feature flags: + # - `v1dns`: + # When this flag is set, Consul agents use the legacy DNS implementation. + # This setting exists in the case a DNS bug is found after the refactoring introduced in v1.19.0. + # + # Example: + # + # ```yaml + # experiments: [ "v1dns" ] + # ``` + # @type: array + experiments: [] + +# Server, when enabled, configures a server cluster to run. This should +# be disabled if you plan on connecting to a Consul cluster external to +# the Kube cluster. +server: + # If true, the chart will install all the resources necessary for a + # Consul server cluster. If you're running Consul externally and want agents + # within Kubernetes to join that cluster, this should probably be false. + # @default: global.enabled + # @type: boolean + enabled: "-" + + # Override global log verbosity level. One of "trace", "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + + # The name of the Docker image (including any tag) for the containers running + # Consul server agents. + # @type: string + image: null + + # The number of server agents to run. This determines the fault tolerance of + # the cluster. Please refer to the [deployment table](https://developer.hashicorp.com/consul/docs/architecture/consensus#deployment-table) + # for more information. + replicas: 1 + + # The number of servers that are expected to be running. + # It defaults to server.replicas. + # In most cases the default should be used, however if there are more + # servers in this datacenter than server.replicas it might make sense + # to override the default. This would be the case if two kube clusters + # were joined into the same datacenter and each cluster ran a certain number + # of servers. + # @type: int + bootstrapExpect: null + + # A secret containing a certificate & key for the server agents to use + # for TLS communication within the Consul cluster. Cert needs to be provided with + # additional DNS name SANs so that it will work within the Kubernetes cluster: + # + # Kubernetes Secrets backend: + # ```bash + # consul tls cert create -server -days=730 -domain=consul -ca=consul-agent-ca.pem \ + # -key=consul-agent-ca-key.pem -dc={{datacenter}} \ + # -additional-dnsname="{{fullname}}-server" \ + # -additional-dnsname="*.{{fullname}}-server" \ + # -additional-dnsname="*.{{fullname}}-server.{{namespace}}" \ + # -additional-dnsname="*.{{fullname}}-server.{{namespace}}.svc" \ + # -additional-dnsname="*.server.{{datacenter}}.{{domain}}" \ + # -additional-dnsname="server.{{datacenter}}.{{domain}}" + # ``` + # + # If you have generated the server-cert yourself with the consul CLI, you could use the following command + # to create the secret in Kubernetes: + # + # ```bash + # kubectl create secret generic consul-server-cert \ + # --from-file='tls.crt=./dc1-server-consul-0.pem' + # --from-file='tls.key=./dc1-server-consul-0-key.pem' + # ``` + # + # Vault Secrets backend: + # If you are using Vault as a secrets backend, a Vault Policy must be created which allows `["create", "update"]` + # capabilities on the PKI issuing endpoint, which is usually of the form `pki/issue/consul-server`. + # Complete [this tutorial](https://developer.hashicorp.com/consul/tutorials/vault-secure/vault-pki-consul-secure-tls) + # to learn how to generate a compatible certificate. + # Note: when using TLS, both the `server.serverCert` and `global.tls.caCert` which points to the CA endpoint of this PKI engine + # must be provided. + serverCert: + # The name of the Vault secret that holds the PEM encoded server certificate. + # @type: string + secretName: null + + # Exposes the servers' gossip and RPC ports as hostPorts. To enable a client + # agent outside of the k8s cluster to join the datacenter, you would need to + # enable `server.exposeGossipAndRPCPorts`, `client.exposeGossipPorts`, and + # set `server.ports.serflan.port` to a port not being used on the host. Since + # `client.exposeGossipPorts` uses the hostPort 8301, + # `server.ports.serflan.port` must be set to something other than 8301. + exposeGossipAndRPCPorts: false + + # Configures ports for the consul servers. + ports: + # Configures the LAN gossip port for the consul servers. If you choose to + # enable `server.exposeGossipAndRPCPorts` and `client.exposeGossipPorts`, + # that will configure the LAN gossip ports on the servers and clients to be + # hostPorts, so if you are running clients and servers on the same node the + # ports will conflict if they are both 8301. When you enable + # `server.exposeGossipAndRPCPorts` and `client.exposeGossipPorts`, you must + # change this from the default to an unused port on the host, e.g. 9301. By + # default the LAN gossip port is 8301 and configured as a containerPort on + # the consul server Pods. + serflan: + port: 8301 + + # This defines the disk size for configuring the + # servers' StatefulSet storage. For dynamically provisioned storage classes, this is the + # desired size. For manually defined persistent volumes, this should be set to + # the disk size of the attached volume. + storage: 10Gi + + # The StorageClass to use for the servers' StatefulSet storage. It must be + # able to be dynamically provisioned if you want the storage + # to be automatically created. For example, to use + # local(https://kubernetes.io/docs/concepts/storage/storage-classes/#local) + # storage classes, the PersistentVolumeClaims would need to be manually created. + # A `null` value will use the Kubernetes cluster's default StorageClass. If a default + # StorageClass does not exist, you will need to create one. + # Refer to the [Read/Write Tuning](https://developer.hashicorp.com/consul/docs/install/performance#read-write-tuning) + # section of the Server Performance Requirements documentation for considerations + # around choosing a performant storage class. + # + # ~> **Note:** The [Reference Architecture](https://developer.hashicorp.com/consul/tutorials/production-deploy/reference-architecture#hardware-sizing-for-consul-servers) + # contains best practices and recommendations for selecting suitable + # hardware sizes for your Consul servers. + # @type: string + storageClass: null + + # The [Persistent Volume Claim (PVC) retention policy](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#persistentvolumeclaim-retention) + # controls if and how PVCs are deleted during the lifecycle of a StatefulSet. + # WhenDeleted specifies what happens to PVCs created from StatefulSet VolumeClaimTemplates when the StatefulSet is deleted, + # and WhenScaled specifies what happens to PVCs created from StatefulSet VolumeClaimTemplates when the StatefulSet is scaled down. + # + # Example: + # + # ```yaml + # persistentVolumeClaimRetentionPolicy: + # whenDeleted: Retain + # whenScaled: Retain + # ``` + # @type: map + persistentVolumeClaimRetentionPolicy: null + + # This will enable/disable [service mesh](https://developer.hashicorp.com/consul/docs/connect). Setting this to true + # _will not_ automatically secure pod communication, this + # setting will only enable usage of the feature. Consul will automatically initialize + # a new CA and set of certificates. Additional service mesh settings can be configured + # by setting the `server.extraConfig` value or by applying [configuration entries](https://developer.hashicorp.com/consul/docs/connect/config-entries). + connect: true + + # When set to true, enables Consul to report additional debugging information, including runtime profiling (pprof) data. + # This setting is only required for clusters without ACL enabled. Sets `enable_debug` in server agent config to `true`. + # If you change this setting, you must restart the agent for the change to take effect. Default is false. + # @type: boolean + enableAgentDebug: false + + serviceAccount: + # This value defines additional annotations for the server service account. This should be formatted as a multi-line + # string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + # The resource requests (CPU, memory, etc.) + # for each of the server agents. This should be a YAML map corresponding to a Kubernetes + # [`ResourceRequirements``](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#resourcerequirements-v1-core) + # object. NOTE: The use of a YAML string is deprecated. + # + # Example: + # + # ```yaml + # resources: + # requests: + # memory: '200Mi' + # cpu: '100m' + # limits: + # memory: '200Mi' + # cpu: '100m' + # ``` + # + # @recurse: false + # @type: map + resources: + requests: + memory: "200Mi" + cpu: "100m" + limits: + memory: "200Mi" + cpu: "100m" + + # The security context for the server pods. This should be a YAML map corresponding to a + # Kubernetes [SecurityContext](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/) object. + # By default, servers will run as non-root, with user ID `100` and group ID `1000`, + # which correspond to the consul user and group created by the Consul docker image. + # Note: if running on OpenShift, this setting is ignored because the user and group are set automatically + # by the OpenShift platform. + # @type: map + # @recurse: false + securityContext: + runAsNonRoot: true + runAsGroup: 1000 + runAsUser: 100 + fsGroup: 1000 + + # The container securityContext for each container in the server pods. In + # addition to the Pod's SecurityContext this can + # set the capabilities of processes running in the container and ensure the + # root file systems in the container is read-only. + # @type: map + # @recurse: true + containerSecurityContext: + # The consul server agent container + # @type: map + # @recurse: false + server: null + # The acl-init job + # @type: map + # @recurse: false + aclInit: null + # The tls-init job + # @type: map + # @recurse: false + tlsInit: null + + # This value is used to carefully + # control a rolling update of Consul server agents. This value specifies the + # [partition](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#partitions) + # for performing a rolling update. Please read the linked Kubernetes + # and [Upgrade Consul](https://developer.hashicorp.com/consul/docs/k8s/upgrade#upgrading-consul-servers) + # documentation for more information. + updatePartition: 0 + + # This configures the [`PodDisruptionBudget`](https://kubernetes.io/docs/tasks/run-application/configure-pdb/) + # for the server cluster. + disruptionBudget: + # Enables registering a PodDisruptionBudget for the server + # cluster. If enabled, it only registers the budget so long as + # the server cluster is enabled. To disable, set to `false`. + enabled: true + + # The maximum number of unavailable pods. In most cases you should not change this as it is automatically set to + # the correct number when left as null. This setting has been kept to preserve backwards compatibility. + # + # By default, this is set to 1 internally in the chart. When server pods are stopped gracefully, they leave the Raft + # consensus pool. When running an odd number of servers, one server leaving the pool does not change the quorum + # size, and so fault tolerance is not affected. However, if more than one server were to leave the pool, the quorum + # size would change. That's why this is set to 1 internally and should not be changed in most cases. + # + # If you need to set this to `0`, you will need to add a + # --set 'server.disruptionBudget.maxUnavailable=0'` flag to the helm chart installation + # command because of a limitation in the Helm templating language. + # @type: integer + maxUnavailable: null + + # A raw string of extra [JSON configuration](https://developer.hashicorp.com/consul/docs/agent/config/config-files) for Consul + # servers. This will be saved as-is into a ConfigMap that is read by the Consul + # server agents. This can be used to add additional configuration that + # isn't directly exposed by the chart. + # + # Example: + # + # ```yaml + # extraConfig: | + # { + # "log_level": "DEBUG" + # } + # ``` + # + # This can also be set using Helm's `--set` flag using the following syntax: + # + # ```shell-session + # --set 'server.extraConfig="{"log_level": "DEBUG"}"' + # ``` + extraConfig: | + {} + + # A list of extra volumes to mount for server agents. This + # is useful for bringing in extra data that can be referenced by other configurations + # at a well known path, such as TLS certificates or Gossip encryption keys. The + # value of this should be a list of objects. + # + # Example: + # + # ```yaml + # extraVolumes: + # - type: secret + # name: consul-certs + # load: false + # ``` + # + # Each object supports the following keys: + # + # - `type` - Type of the volume, must be one of "configMap" or "secret". Case sensitive. + # + # - `name` - Name of the configMap or secret to be mounted. This also controls + # the path that it is mounted to. The volume will be mounted to `/consul/userconfig/`. + # + # - `load` - If true, then the agent will be + # configured to automatically load HCL/JSON configuration files from this volume + # with `-config-dir`. This defaults to false. + # + # @type: array + extraVolumes: [] + + # A list of sidecar containers. + # Example: + # + # ```yaml + # extraContainers: + # - name: extra-container + # image: example-image:latest + # command: + # - ... + # ``` + # @type: array + extraContainers: [] + + # This value defines the [affinity](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity) + # for server pods. It defaults to allowing only a single server pod on each node, which + # minimizes risk of the cluster becoming unusable if a node is lost. If you need + # to run more pods per node (for example, testing on Minikube), set this value + # to `null`. + # + # Example: + # + # ```yaml + # affinity: | + # podAntiAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # - labelSelector: + # matchLabels: + # app: {{ template "consul.name" . }} + # release: "{{ .Release.Name }}" + # component: server + # topologyKey: kubernetes.io/hostname + # ``` + affinity: | + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: + app: {{ template "consul.name" . }} + release: "{{ .Release.Name }}" + component: server + topologyKey: kubernetes.io/hostname + + # Toleration settings for server pods. This + # should be a multi-line string matching the + # [Tolerations](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) + # array in a Pod spec. + tolerations: "" + + # Pod topology spread constraints for server pods. + # This should be a multi-line YAML string matching the + # [`topologySpreadConstraints`](https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/) + # array in a Pod Spec. + # + # This requires K8S >= 1.18 (beta) or 1.19 (stable). + # + # Example: + # + # ```yaml + # topologySpreadConstraints: | + # - maxSkew: 1 + # topologyKey: topology.kubernetes.io/zone + # whenUnsatisfiable: DoNotSchedule + # labelSelector: + # matchLabels: + # app: {{ template "consul.name" . }} + # release: "{{ .Release.Name }}" + # component: server + # ``` + topologySpreadConstraints: "" + + # This value defines [`nodeSelector`](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) + # labels for server pod assignment, formatted as a multi-line string. + # + # Example: + # + # ```yaml + # nodeSelector: | + # beta.kubernetes.io/arch: amd64 + # ``` + # + # @type: string + nodeSelector: null + + # This value references an existing + # Kubernetes [`priorityClassName`](https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#pod-priority) + # that can be assigned to server pods. + priorityClassName: "" + + # Extra labels to attach to the server pods. This should be a YAML map. + # + # Example: + # + # ```yaml + # extraLabels: + # labelKey: label-value + # anotherLabelKey: another-label-value + # ``` + # + # @type: map + extraLabels: null + + # This value defines additional annotations for + # server pods. This should be formatted as a multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + # Configures a service to expose ports on the Consul servers over a Kubernetes Service. + exposeService: + # When enabled, deploys a Kubernetes Service to reach the Consul servers. + # @type: boolean + enabled: "-" + # Type of service, supports LoadBalancer or NodePort. + # @type: string + type: LoadBalancer + # If service is of type NodePort, configures the nodePorts. + nodePort: + # Configures the nodePort to expose the Consul server http port. + # @type: integer + http: null + # Configures the nodePort to expose the Consul server https port. + # @type: integer + https: null + # Configures the nodePort to expose the Consul server serf port. + # @type: integer + serf: null + # Configures the nodePort to expose the Consul server rpc port. + # @type: integer + rpc: null + # Configures the nodePort to expose the Consul server grpc port. + # @type: integer + grpc: null + # This value defines additional annotations for + # server pods. This should be formatted as a multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + # Server service properties. + service: + # Annotations to apply to the server service. + # + # ```yaml + # annotations: | + # "annotation-key": "annotation-value" + # ``` + # + # @type: string + annotations: null + + # A list of extra environment variables to set within the stateful set. + # These could be used to include proxy settings required for cloud auto-join + # feature, in case kubernetes cluster is behind egress http proxies. Additionally, + # it could be used to configure custom consul parameters. + # @type: map + extraEnvironmentVars: {} + + # [Enterprise Only] Values for setting up and running + # [snapshot agents](https://developer.hashicorp.com/consul/commands/snapshot/agent) + # within the Consul clusters. They run as a sidecar with Consul servers. + snapshotAgent: + # If true, the chart will install resources necessary to run the snapshot agent. + enabled: false + + # Interval at which to perform snapshots. + # Refer to [`interval`](https://developer.hashicorp.com/consul/commands/snapshot/agent#interval) + # @type: string + interval: 1h + + # A Kubernetes or Vault secret that should be manually created to contain the entire + # config to be used on the snapshot agent. + # This is the preferred method of configuration since there are usually storage + # credentials present. Please refer to the [Snapshot agent config](https://developer.hashicorp.com/consul/commands/snapshot/agent#config-file-options) + # for details. + configSecret: + # The name of the Kubernetes secret or Vault secret path that holds the snapshot agent config. + # @type: string + secretName: null + # The key within the Kubernetes secret or Vault secret key that holds the snapshot agent config. + # @type: string + secretKey: null + + # The resource settings for snapshot agent pods. + # @recurse: false + # @type: map + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" + + # Optional PEM-encoded CA certificate that will be added to the trusted system CAs. + # Useful if using an S3-compatible storage exposing a self-signed certificate. + # + # Example: + # + # ```yaml + # caCert: | + # -----BEGIN CERTIFICATE----- + # MIIC7jCCApSgAwIBAgIRAIq2zQEVexqxvtxP6J0bXAwwCgYIKoZIzj0EAwIwgbkx + # ... + # ``` + # @type: string + caCert: null + + # [Enterprise Only] Added in Consul 1.8, the audit object allow users to enable auditing + # and configure a sink and filters for their audit logs. Please refer to + # [audit logs](https://developer.hashicorp.com/consul/docs/enterprise/audit-logging) documentation + # for further information. + auditLogs: + # Controls whether Consul logs out each time a user performs an operation. + # global.acls.manageSystemACLs must be enabled to use this feature. + enabled: false + + # A single entry of the sink object provides configuration for the destination to which Consul + # will log auditing events. + # + # Example: + # + # ```yaml + # sinks: + # - name: My Sink + # type: file + # format: json + # path: /tmp/audit.json + # delivery_guarantee: best-effort + # rotate_duration: 24h + # rotate_max_files: 15 + # rotate_bytes: 25165824 + # + # ``` + # + # The sink object supports the following keys: + # + # - `name` - Name of the sink. + # + # - `type` - Type specifies what kind of sink this is. Currently only file sinks are available + # + # - `format` - Format specifies what format the events will be emitted with. Currently only `json` + # events are emitted. + # + # - `path` - The directory and filename to write audit events to. + # + # - `delivery_guarantee` - Specifies the rules governing how audit events are written. Consul + # only supports `best-effort` event delivery. + # + # - `mode` - The permissions to set on the audit log files. + # + # - `rotate_duration` - Specifies the interval by which the system rotates to a new log file. + # At least one of `rotate_duration` or `rotate_bytes` must be configured to enable audit logging. + # + # - `rotate_bytes` - Specifies how large an individual log file can grow before Consul rotates to a new file. + # At least one of rotate_bytes or rotate_duration must be configured to enable audit logging. + # + # - `rotate_max_files` - Defines the limit that Consul should follow before it deletes old log files. + # + # @type: array + sinks: [] + + # Settings for potentially limiting timeouts, rate limiting on clients as well + # as servers, and other settings to limit exposure too many requests, requests + # waiting for too long, and other runtime considerations. + limits: + # This object specifies configurations that limit the rate of RPC and gRPC + # requests on the Consul server. Limiting the rate of gRPC and RPC requests + # also limits HTTP requests to the Consul server. + # https://developer.hashicorp.com/consul/docs/agent/config/config-files#request_limits + requestLimits: + # Setting for disabling or enabling rate limiting. If not disabled, it + # enforces the action that will occur when RequestLimitsReadRate + # or RequestLimitsWriteRate is exceeded. The default value of "disabled" will + # prevent any rate limiting from occuring. A value of "enforce" will block + # the request from processings by returning an error. A value of + # "permissive" will not block the request and will allow the request to + # continue processing. + # @type: string + mode: "disabled" + + # Setting that controls how frequently RPC, gRPC, and HTTP + # queries are allowed to happen. In any large enough time interval, rate + # limiter limits the rate to RequestLimitsReadRate tokens per second. + # + # See https://en.wikipedia.org/wiki/Token_bucket for more about token + # buckets. + # @type: integer + readRate: -1 + + # Setting that controls how frequently RPC, gRPC, and HTTP + # writes are allowed to happen. In any large enough time interval, rate + # limiter limits the rate to RequestLimitsWriteRate tokens per second. + # + # See https://en.wikipedia.org/wiki/Token_bucket for more about token + # buckets. + # @type: integer + writeRate: -1 + +# Configuration for Consul servers when the servers are running outside of Kubernetes. +# When running external servers, configuring these values is recommended +# if setting `global.tls.enableAutoEncrypt` to true +# or `global.acls.manageSystemACLs` to true. +externalServers: + # If true, the Helm chart will be configured to talk to the external servers. + # If setting this to true, you must also set `server.enabled` to false. + enabled: false + + # An array of external Consul server hosts that are used to make + # HTTPS connections from the components in this Helm chart. + # Valid values include an IP, a DNS name, or an [exec=](https://github.com/hashicorp/go-netaddrs) string. + # The port must be provided separately below. + # Note: This slice can only contain a single element. + # Note: If enabling clients, `client.join` must also be set to the hosts that should be + # used to join the cluster. In most cases, the `client.join` values + # should be the same, however, they may be different if you + # wish to use separate hosts for the HTTPS connections. `tlsServerName` is required if TLS is enabled and 'hosts' is not a DNS name. + # @type: array + hosts: [] + + # The HTTPS port of the Consul servers. + httpsPort: 8501 + + # The GRPC port of the Consul servers. + grpcPort: 8502 + + # The server name to use as the SNI host header when connecting with HTTPS. This name also appears as the hostname in the server certificate's subject field. + # @type: string + tlsServerName: null + + # If true, consul-k8s-control-plane components will ignore the CA set in + # `global.tls.caCert` when making HTTPS calls to Consul servers and + # will instead use the consul-k8s-control-plane image's system CAs for TLS verification. + # If false, consul-k8s-control-plane components will use `global.tls.caCert` when + # making HTTPS calls to Consul servers. + # **NOTE:** This does not affect Consul's internal RPC communication which will + # always use `global.tls.caCert`. + useSystemRoots: false + + # If you are setting `global.acls.manageSystemACLs` and + # `connectInject.enabled` to true, set `k8sAuthMethodHost` to the address of the Kubernetes API server. + # This address must be reachable from the Consul servers. + # Please refer to the [Kubernetes Auth Method documentation](https://developer.hashicorp.com/consul/docs/security/acl/auth-methods/kubernetes). + # + # If `global.federation.enabled` is set to true, `global.federation.k8sAuthMethodHost` and + # `externalServers.k8sAuthMethodHost` should be set to the same value. + # + # You could retrieve this value from your `kubeconfig` by running: + # + # ```shell-session + # $ kubectl config view \ + # -o jsonpath="{.clusters[?(@.name=='')].cluster.server}" + # ``` + # + # @type: string + k8sAuthMethodHost: null + + # If true, setting this prevents the consul-dataplane and consul-k8s components from watching the Consul servers for changes. This is + # useful for situations where Consul servers are behind a load balancer. + skipServerWatch: false + +# Values that configure running a Consul client on Kubernetes nodes. +client: + # If true, the chart will install all + # the resources necessary for a Consul client on every Kubernetes node. This _does not_ require + # `server.enabled`, since the agents can be configured to join an external cluster. + # @type: boolean + enabled: false + + # Override global log verbosity level. One of "trace", "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + + # The name of the Docker image (including any tag) for the containers + # running Consul client agents. + # @type: string + image: null + + # A list of valid [`-retry-join` values](https://developer.hashicorp.com/consul/docs/agent/config/cli-flags#_retry_join). + # If this is `null` (default), then the clients will attempt to automatically + # join the server cluster running within Kubernetes. + # This means that with `server.enabled` set to true, clients will automatically + # join that cluster. If `server.enabled` is not true, then a value must be + # specified so the clients can join a valid cluster. + # @type: array + join: null + + # An absolute path to a directory on the host machine to use as the Consul + # client data directory. If set to the empty string or null, the Consul agent + # will store its data in the Pod's local filesystem (which will + # be lost if the Pod is deleted). Security Warning: If setting this, Pod Security + # Policies _must_ be enabled on your cluster and in this Helm chart (via the + # `global.enablePodSecurityPolicies` setting) to prevent other pods from + # mounting the same host path and gaining access to all of Consul's data. + # Consul's data is not encrypted at rest. + # @type: string + dataDirectoryHostPath: null + + # If true, agents will enable their GRPC listener on + # port 8502 and expose it to the host. This will use slightly more resources, but is + # required for Connect. + grpc: true + + # nodeMeta specifies an arbitrary metadata key/value pair to associate with the node + # (refer to [`-node-meta`](https://developer.hashicorp.com/consul/docs/agent/config/cli-flags#_node_meta)) + nodeMeta: + pod-name: ${HOSTNAME} + host-ip: ${HOST_IP} + + # If true, the Helm chart will expose the clients' gossip ports as hostPorts. + # This is only necessary if pod IPs in the k8s cluster are not directly routable + # and the Consul servers are outside of the k8s cluster. + # This also changes the clients' advertised IP to the `hostIP` rather than `podIP`. + exposeGossipPorts: false + + serviceAccount: + # This value defines additional annotations for the client service account. This should be formatted as a multi-line + # string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + # The resource settings for Client agents. + # NOTE: The use of a YAML string is deprecated. Instead, set directly as a + # YAML map. + # @recurse: false + # @type: map + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "100Mi" + cpu: "100m" + + # The security context for the client pods. This should be a YAML map corresponding to a + # Kubernetes [SecurityContext](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/) object. + # By default, servers will run as non-root, with user ID `100` and group ID `1000`, + # which correspond to the consul user and group created by the Consul docker image. + # Note: if running on OpenShift, this setting is ignored because the user and group are set automatically + # by the OpenShift platform. + # @type: map + # @recurse: false + securityContext: + runAsNonRoot: true + runAsGroup: 1000 + runAsUser: 100 + fsGroup: 1000 + + # The container securityContext for each container in the client pods. In + # addition to the Pod's SecurityContext this can + # set the capabilities of processes running in the container and ensure the + # root file systems in the container is read-only. + # @type: map + # @recurse: true + containerSecurityContext: + # The consul client agent container + # @type: map + # @recurse: false + client: null + # The acl-init initContainer + # @type: map + # @recurse: false + aclInit: null + # The tls-init initContainer + # @type: map + # @recurse: false + tlsInit: null + + # A raw string of extra [JSON configuration](https://developer.hashicorp.com/consul/docs/agent/config/config-files) for Consul + # clients. This will be saved as-is into a ConfigMap that is read by the Consul + # client agents. This can be used to add additional configuration that + # isn't directly exposed by the chart. + # + # Example: + # + # ```yaml + # extraConfig: | + # { + # "log_level": "DEBUG" + # } + # ``` + # + # This can also be set using Helm's `--set` flag using the following syntax: + # + # ```shell-session + # --set 'client.extraConfig="{"log_level": "DEBUG"}"' + # ``` + extraConfig: | + {} + + # A list of extra volumes to mount for client agents. This + # is useful for bringing in extra data that can be referenced by other configurations + # at a well known path, such as TLS certificates or Gossip encryption keys. The + # value of this should be a list of objects. + # + # Example: + # + # ```yaml + # extraVolumes: + # - type: secret + # name: consul-certs + # load: false + # ``` + # + # Each object supports the following keys: + # + # - `type` - Type of the volume, must be one of "configMap" or "secret". Case sensitive. + # + # - `name` - Name of the configMap or secret to be mounted. This also controls + # the path that it is mounted to. The volume will be mounted to `/consul/userconfig/`. + # + # - `load` - If true, then the agent will be + # configured to automatically load HCL/JSON configuration files from this volume + # with `-config-dir`. This defaults to false. + # + # @type: array + extraVolumes: [] + + # A list of sidecar containers. + # Example: + # + # ```yaml + # extraContainers: + # - name: extra-container + # image: example-image:latest + # command: + # - ... + # ``` + # @type: array + extraContainers: [] + + # Toleration Settings for Client pods + # This should be a multi-line string matching the Toleration array + # in a PodSpec. + # The example below will allow Client pods to run on every node + # regardless of taints + # + # ```yaml + # tolerations: | + # - operator: Exists + # ``` + tolerations: "" + + # nodeSelector labels for client pod assignment, formatted as a multi-line string. + # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + # + # Example: + # + # ```yaml + # nodeSelector: | + # beta.kubernetes.io/arch: amd64 + # ``` + # @type: string + nodeSelector: null + + # Affinity Settings for Client pods, formatted as a multi-line YAML string. + # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity + # + # Example: + # + # ```yaml + # affinity: | + # nodeAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # nodeSelectorTerms: + # - matchExpressions: + # - key: node-role.kubernetes.io/master + # operator: DoesNotExist + # ``` + # @type: string + affinity: null + + # This value references an existing + # Kubernetes [`priorityClassName`](https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#pod-priority) + # that can be assigned to client pods. + priorityClassName: "" + + # This value defines additional annotations for + # client pods. This should be formatted as a multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + # Extra labels to attach to the client pods. This should be a regular YAML map. + # + # Example: + # + # ```yaml + # extraLabels: + # labelKey: label-value + # anotherLabelKey: another-label-value + # ``` + # + # @type: map + extraLabels: null + + # A list of extra environment variables to set within the stateful set. + # These could be used to include proxy settings required for cloud auto-join + # feature, in case kubernetes cluster is behind egress http proxies. Additionally, + # it could be used to configure custom consul parameters. + # @type: map + extraEnvironmentVars: {} + + # This value defines the [Pod DNS policy](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy) + # for client pods to use. + # @type: string + dnsPolicy: null + + # hostNetwork defines whether or not we use host networking instead of hostPort in the event + # that a CNI plugin doesn't support `hostPort`. This has security implications and is not recommended + # as doing so gives the consul client unnecessary access to all network traffic on the host. + # In most cases, pod network and host network are on different networks so this should be + # combined with `dnsPolicy: ClusterFirstWithHostNet` + hostNetwork: false + + # updateStrategy for the DaemonSet. + # Refer to the Kubernetes [Daemonset upgrade strategy](https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/#daemonset-update-strategy) + # documentation. + # This should be a multi-line string mapping directly to the updateStrategy + # + # Example: + # + # ```yaml + # updateStrategy: | + # rollingUpdate: + # maxUnavailable: 5 + # type: RollingUpdate + # ``` + # + # @type: string + updateStrategy: null + +# Configuration for DNS configuration within the Kubernetes cluster. +# This creates a service that routes to all agents (client or server) +# for serving DNS requests. This DOES NOT automatically configure kube-dns +# today, so you must still manually configure a `stubDomain` with kube-dns +# for this to have any effect: +# https://kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/#configure-stub-domain-and-upstream-dns-servers +dns: + # @type: boolean + enabled: "-" + + # If true, services using Consul service mesh will use Consul DNS + # for default DNS resolution. The DNS lookups fall back to the nameserver IPs + # listed in /etc/resolv.conf if not found in Consul. + # @type: boolean + enableRedirection: "-" + + # Used to control the type of service created. For + # example, setting this to "LoadBalancer" will create an external load + # balancer (for supported K8S installations) + type: ClusterIP + + # Set a predefined cluster IP for the DNS service. + # Useful if you need to reference the DNS service's IP + # address in CoreDNS config. + # @type: string + clusterIP: null + + # Extra annotations to attach to the dns service + # This should be a multi-line string of + # annotations to apply to the dns Service + # @type: string + annotations: null + + # Additional ServiceSpec values + # This should be a multi-line string mapping directly to a Kubernetes + # ServiceSpec object. + # @type: string + additionalSpec: null + +# Values that configure the Consul UI. +ui: + # If true, the UI will be enabled. This will + # only _enable_ the UI, it doesn't automatically register any service for external + # access. The UI will only be enabled on server agents. If `server.enabled` is + # false, then this setting has no effect. To expose the UI in some way, you must + # configure `ui.service`. + # @default: global.enabled + # @type: boolean + enabled: "-" + + # Configure the service for the Consul UI. + service: + # This will enable/disable registering a + # Kubernetes Service for the Consul UI. This value only takes effect if `ui.enabled` is + # true and taking effect. + enabled: true + + # The service type to register. + # @type: string + type: null + + # Set the port value of the UI service. + port: + # HTTP port. + http: 80 + + # HTTPS port. + https: 443 + + # Optionally set the nodePort value of the ui service if using a NodePort service. + # If not set and using a NodePort service, Kubernetes will automatically assign + # a port. + nodePort: + # HTTP node port + # @type: integer + http: null + + # HTTPS node port + # @type: integer + https: null + + # Annotations to apply to the UI service. + # + # Example: + # + # ```yaml + # annotations: | + # 'annotation-key': annotation-value + # ``` + # @type: string + annotations: null + + # Additional ServiceSpec values + # This should be a multi-line string mapping directly to a Kubernetes + # ServiceSpec object. + # @type: string + additionalSpec: null + + # Configure Ingress for the Consul UI. + # If `global.tls.enabled` is set to `true`, the Ingress will expose + # the port 443 on the UI service. Please ensure the Ingress Controller + # supports SSL pass-through and it is enabled to ensure traffic forwarded + # to port 443 has not been TLS terminated. + ingress: + # This will create an Ingress resource for the Consul UI. + # @type: boolean + enabled: false + + # Optionally set the ingressClassName. + ingressClassName: "" + + # pathType override - refer to: https://kubernetes.io/docs/concepts/services-networking/ingress/#path-types + pathType: Prefix + + # hosts is a list of host name to create Ingress rules. + # + # ```yaml + # hosts: + # - host: foo.bar + # paths: + # - /example + # - /test + # ``` + # + # @type: array + hosts: [] + + # tls is a list of hosts and secret name in an Ingress + # which tells the Ingress controller to secure the channel. + # + # ```yaml + # tls: + # - hosts: + # - chart-example.local + # secretName: testsecret-tls + # ``` + # @type: array + tls: [] + + # Annotations to apply to the UI ingress. + # + # Example: + # + # ```yaml + # annotations: | + # 'annotation-key': annotation-value + # ``` + # @type: string + annotations: null + + # Configurations for displaying metrics in the UI. + metrics: + # Enable displaying metrics in the UI. The default value of "-" + # will inherit from `global.metrics.enabled` value. + # @type: boolean + # @default: global.metrics.enabled + enabled: "-" + # Provider for metrics. Refer to + # [`metrics_provider`](https://developer.hashicorp.com/consul/docs/agent/config/config-files#ui_config_metrics_provider) + # This value is only used if `ui.enabled` is set to true. + # @type: string + provider: "prometheus" + + # baseURL is the URL of the prometheus server, usually the service URL. + # This value is only used if `ui.enabled` is set to true. + # @type: string + baseURL: http://prometheus-server + + # Corresponds to [`dashboard_url_templates`](https://developer.hashicorp.com/consul/docs/agent/config/config-files#ui_config_dashboard_url_templates) + # configuration. + dashboardURLTemplates: + # Sets [`dashboardURLTemplates.service`](https://developer.hashicorp.com/consul/docs/agent/config/config-files#ui_config_dashboard_url_templates_service). + service: "" + +# Configure the catalog sync process to sync K8S with Consul +# services. This can run bidirectional (default) or unidirectionally (Consul +# to K8S or K8S to Consul only). +# +# This process assumes that a Consul agent is available on the host IP. +# This is done automatically if clients are enabled. If clients are not +# enabled then set the node selection so that it chooses a node with a +# Consul agent. +syncCatalog: + # True if you want to enable the catalog sync. Set to "-" to inherit from + # global.enabled. + enabled: false + + # The name of the Docker image (including any tag) for consul-k8s-control-plane + # to run the sync program. + # @type: string + image: null + + # If true, all valid services in K8S are + # synced by default. If false, the service must be [annotated](https://developer.hashicorp.com/consul/docs/k8s/service-sync#enable-and-disable-sync) + # properly to sync. + # In either case an annotation can override the default. + default: true + + # Optional priorityClassName. + priorityClassName: "" + + # If true, will sync Kubernetes services to Consul. This can be disabled to + # have a one-way sync. + toConsul: true + + # If true, will sync Consul services to Kubernetes. This can be disabled to + # have a one-way sync. + toK8S: true + + # Service prefix to prepend to services before registering + # with Kubernetes. For example "consul-" will register all services + # prepended with "consul-". (Consul -> Kubernetes sync) + # @type: string + k8sPrefix: null + + # List of k8s namespaces to sync the k8s services from. + # If a k8s namespace is not included in this list or is listed in `k8sDenyNamespaces`, + # services in that k8s namespace will not be synced even if they are explicitly + # annotated. Use `["*"]` to automatically allow all k8s namespaces. + # + # For example, `["namespace1", "namespace2"]` will only allow services in the k8s + # namespaces `namespace1` and `namespace2` to be synced and registered + # with Consul. All other k8s namespaces will be ignored. + # + # To deny all namespaces, set this to `[]`. + # + # Note: `k8sDenyNamespaces` takes precedence over values defined here. + # @type: array + k8sAllowNamespaces: ["*"] + + # List of k8s namespaces that should not have their + # services synced. This list takes precedence over `k8sAllowNamespaces`. + # `*` is not supported because then nothing would be allowed to sync. + # + # For example, if `k8sAllowNamespaces` is `["*"]` and `k8sDenyNamespaces` is + # `["namespace1", "namespace2"]`, then all k8s namespaces besides `namespace1` + # and `namespace2` will be synced. + # @type: array + k8sDenyNamespaces: ["kube-system", "kube-public"] + + # [DEPRECATED] Use k8sAllowNamespaces and k8sDenyNamespaces instead. For + # backwards compatibility, if both this and the allow/deny lists are set, + # the allow/deny lists will be ignored. + # k8sSourceNamespace is the Kubernetes namespace to watch for service + # changes and sync to Consul. If this is not set then it will default + # to all namespaces. + # @type: string + k8sSourceNamespace: null + + # [Enterprise Only] These settings manage the catalog sync's interaction with + # Consul namespaces (requires consul-ent v1.7+). + # Also, `global.enableConsulNamespaces` must be true. + consulNamespaces: + # Name of the Consul namespace to register all + # k8s services into. If the Consul namespace does not already exist, + # it will be created. This will be ignored if `mirroringK8S` is true. + consulDestinationNamespace: "default" + + # If true, k8s services will be registered into a Consul namespace + # of the same name as their k8s namespace, optionally prefixed if + # `mirroringK8SPrefix` is set below. If the Consul namespace does not + # already exist, it will be created. Turning this on overrides the + # `consulDestinationNamespace` setting. + # `addK8SNamespaceSuffix` may no longer be needed if enabling this option. + # If mirroring is enabled, avoid creating any Consul resources in the following + # Kubernetes namespaces, as Consul currently reserves these namespaces for + # system use: "system", "universal", "operator", "root". + mirroringK8S: true + + # If `mirroringK8S` is set to true, `mirroringK8SPrefix` allows each Consul namespace + # to be given a prefix. For example, if `mirroringK8SPrefix` is set to "k8s-", a + # service in the k8s `staging` namespace will be registered into the + # `k8s-staging` Consul namespace. + mirroringK8SPrefix: "" + + # Appends Kubernetes namespace suffix to + # each service name synced to Consul, separated by a dash. + # For example, for a service 'foo' in the default namespace, + # the sync process will create a Consul service named 'foo-default'. + # Set this flag to true to avoid registering services with the same name + # but in different namespaces as instances for the same Consul service. + # Namespace suffix is not added if 'annotationServiceName' is provided. + addK8SNamespaceSuffix: true + + # Service prefix which prepends itself + # to Kubernetes services registered within Consul + # For example, "k8s-" will register all services prepended with "k8s-". + # (Kubernetes -> Consul sync) + # consulPrefix is ignored when 'annotationServiceName' is provided. + # NOTE: Updating this property to a non-null value for an existing installation will result in deregistering + # of existing services in Consul and registering them with a new name. + # @type: string + consulPrefix: null + + # Optional tag that is applied to all of the Kubernetes services + # that are synced into Consul. If nothing is set, defaults to "k8s". + # (Kubernetes -> Consul sync) + # @type: string + k8sTag: null + + # Defines the Consul synthetic node that all services + # will be registered to. + # NOTE: Changing the node name and upgrading the Helm chart will leave + # all of the previously sync'd services registered with Consul and + # register them again under the new Consul node name. The out-of-date + # registrations will need to be explicitly removed. + consulNodeName: "k8s-sync" + + # Syncs services of the ClusterIP type, which may + # or may not be broadly accessible depending on your Kubernetes cluster. + # Set this to false to skip syncing ClusterIP services. + syncClusterIPServices: true + + # If true, LoadBalancer service endpoints instead of ingress addresses will be synced to Consul. + # If false, LoadBalancer endpoints are not synced to Consul. + syncLoadBalancerEndpoints: false + + ingress: + # Syncs the hostname from a Kubernetes Ingress resource to service registrations + # when a rule matched a service. Currently only supports host based routing and + # not path based routing. The only supported path on an ingress rule is "/". + # Set this to false to skip syncing Ingress services. + # + # Currently, port 80 is synced if there is not TLS entry for the hostname. Syncs the port + # 443 if there is a TLS entry that matches the hostname. + enabled: false + # Requires syncIngress to be `true`. syncs the LoadBalancer IP from a Kubernetes Ingress + # resource instead of the hostname to service registrations when a rule matched a service. + loadBalancerIPs: false + + # Configures the type of syncing that happens for NodePort + # services. The valid options are: ExternalOnly, InternalOnly, ExternalFirst. + # + # - ExternalOnly will only use a node's ExternalIP address for the sync + # - InternalOnly use's the node's InternalIP address + # - ExternalFirst will preferentially use the node's ExternalIP address, but + # if it doesn't exist, it will use the node's InternalIP address instead. + nodePortSyncType: ExternalFirst + + # Refers to a Kubernetes secret that you have created that contains + # an ACL token for your Consul cluster which allows the sync process the correct + # permissions. This is only needed if ACLs are managed manually within the Consul cluster, i.e. `global.acls.manageSystemACLs` is `false`. + aclSyncToken: + # The name of the Kubernetes secret that holds the acl sync token. + # @type: string + secretName: null + # The key within the Kubernetes secret that holds the acl sync token. + # @type: string + secretKey: null + + # This value defines [`nodeSelector`](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) + # labels for catalog sync pod assignment, formatted as a multi-line string. + # + # Example: + # + # ```yaml + # nodeSelector: | + # beta.kubernetes.io/arch: amd64 + # ``` + # + # @type: string + nodeSelector: null + + # Affinity Settings + # This should be a multi-line string matching the affinity object + # @type: string + affinity: null + + # Toleration Settings + # This should be a multi-line string matching the Toleration array + # in a PodSpec. + # @type: string + tolerations: null + + serviceAccount: + # This value defines additional annotations for the mesh gateways' service account. This should be formatted as a + # multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + # The resource settings for sync catalog pods. + # @recurse: false + # @type: map + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" + + # Override global log verbosity level. One of "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + + # Override the default interval to perform syncing operations creating Consul services. + # @type: string + consulWriteInterval: null + + # Extra labels to attach to the sync catalog pods. This should be a YAML map. + # + # Example: + # + # ```yaml + # extraLabels: + # labelKey: label-value + # anotherLabelKey: another-label-value + # ``` + # + # @type: map + extraLabels: null + + # This value defines additional annotations for + # the catalog sync pods. This should be formatted as a multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + +# Configures the automatic Connect sidecar injector. +connectInject: + # True if you want to enable connect injection. Set to "-" to inherit from + # global.enabled. + enabled: true + + # The number of deployment replicas. + replicas: 1 + + # Image for consul-k8s-control-plane that contains the injector. + # @type: string + image: null + + # If true, the injector will inject the + # Connect sidecar into all pods by default. Otherwise, pods must specify the + # [injection annotation](https://developer.hashicorp.com/consul/docs/k8s/connect#consul-hashicorp-com-connect-inject) + # to opt-in to Connect injection. If this is true, pods can use the same annotation + # to explicitly opt-out of injection. + default: false + + # Configures Transparent Proxy for Consul Service mesh services. + # Using this feature requires Consul 1.10.0-beta1+. + transparentProxy: + # If true, then all Consul Service mesh will run with transparent proxy enabled by default, + # i.e. we enforce that all traffic within the pod will go through the proxy. + # This value is overridable via the "consul.hashicorp.com/transparent-proxy" pod annotation. + defaultEnabled: true + + # If true, we will overwrite Kubernetes HTTP probes of the pod to point to the Envoy proxy instead. + # This setting is recommended because with traffic being enforced to go through the Envoy proxy, + # the probes on the pod will fail because kube-proxy doesn't have the right certificates + # to talk to Envoy. + # This value is also overridable via the "consul.hashicorp.com/transparent-proxy-overwrite-probes" annotation. + # Note: This value has no effect if transparent proxy is disabled on the pod. + defaultOverwriteProbes: true + + # This configures the [`PodDisruptionBudget`](https://kubernetes.io/docs/tasks/run-application/configure-pdb/) + # for the service mesh sidecar injector. + disruptionBudget: + # This will enable/disable registering a PodDisruptionBudget for the + # service mesh sidecar injector. If this is enabled, it will only register the budget so long as + # the service mesh is enabled. + enabled: true + + # The maximum number of unavailable pods. By default, this will be + # automatically computed based on the `connectInject.replicas` value to be `(n/2)-1`. + # If you need to set this to `0`, you will need to add a + # --set 'connectInject.disruptionBudget.maxUnavailable=0'` flag to the helm chart installation + # command because of a limitation in the Helm templating language. + # @type: integer + maxUnavailable: null + + # The minimum number of available pods. + # Takes precedence over maxUnavailable if set. + # @type: integer + minAvailable: null + + # Configuration settings for the Consul API Gateway integration. + apiGateway: + # Enables Consul on Kubernetes to manage the CRDs used for Gateway API. + # Setting this to true will install the CRDs used for the Gateway API when Consul on Kubernetes is installed. + # These CRDs can clash with existing Gateway API CRDs if they are already installed in your cluster. + # If this setting is false, you will need to install the Gateway API CRDs manually. + manageExternalCRDs: true + + # Enables Consul on Kubernets to manage only the non-standard CRDs used for Gateway API. If manageExternalCRDs is true + # then all CRDs will be installed; otherwise, if manageNonStandardCRDs is true then only TCPRoute, GatewayClassConfig and MeshService + # will be installed. + manageNonStandardCRDs: false + + # Configuration settings for the GatewayClass installed by Consul on Kubernetes. + managedGatewayClass: + # This value defines [`nodeSelector`](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) + # labels for gateway pod assignment, formatted as a multi-line string. + # + # Example: + # + # ```yaml + # nodeSelector: | + # beta.kubernetes.io/arch: amd64 + # ``` + # + # @type: string + nodeSelector: null + + # Toleration settings for gateway pods created with the managed gateway class. + # This should be a multi-line string matching the + # [Tolerations](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) array in a Pod spec. + # + # @type: string + tolerations: null + + # This value defines the type of Service created for gateways (e.g. LoadBalancer, ClusterIP) + serviceType: LoadBalancer + + # Configuration settings for annotations to be copied from the Gateway to other child resources. + copyAnnotations: + # This value defines a list of annotations to be copied from the Gateway to the Service created, formatted as a multi-line string. + # + # Example: + # + # ```yaml + # service: + # annotations: | + # - external-dns.alpha.kubernetes.io/hostname + # ``` + # + # @type: string + service: null + + # Metrics settings for gateways created with this gateway class configuration. + metrics: + # This value enables or disables metrics collection on a gateway, overriding the global gateway metrics collection settings. + # @type: boolean + enabled: "-" + # This value sets the port to use for scraping gateway metrics via prometheus, defaults to 20200 if not set. Must be in the port + # range of 1024-65535. + # @type: int + port: null + # This value sets the path to use for scraping gateway metrics via prometheus, defaults to /metrics if not set. + # @type: string + path: null + + # The resource settings for Pods handling traffic for Gateway API. + # @recurse: false + # @type: map + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "100Mi" + cpu: "100m" + + # This value defines the number of pods to deploy for each Gateway as well as a min and max number of pods for all Gateways + deployment: + defaultInstances: 1 + maxInstances: 1 + minInstances: 1 + + # The name of the OpenShift SecurityContextConstraints resource to use for Gateways. + # Only applicable if `global.openshift.enabled` is true. + # @type: string + openshiftSCCName: "restricted-v2" + + # This value defines the amount we will add to privileged container ports on gateways that use this class. + # This is useful if you don't want to give your containers extra permissions to run privileged ports. + # Example: The gateway listener is defined on port 80, but the underlying value of the port on the container + # will be the 80 + the number defined below. + mapPrivilegedContainerPorts: 0 + + # This value contains settings related to the gateway_resources_job that runs on helm install + resourceJob: + # The resource requests (CPU, memory, etc.) for the server-acl-init and server-acl-init-cleanup pods. + # This should be a YAML map corresponding to a Kubernetes + # [`ResourceRequirements``](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#resourcerequirements-v1-core) + # object. + # + # Example: + # + # ```yaml + # resources: + # requests: + # memory: '200Mi' + # cpu: '100m' + # limits: + # memory: '200Mi' + # cpu: '100m' + # ``` + # + # @recurse: false + # @type: map + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" + + # Configuration for the ServiceAccount created for the api-gateway component + serviceAccount: + # This value defines additional annotations for the client service account. This should be formatted as a multi-line + # string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + # Configures consul-cni plugin for Consul Service mesh services + cni: + # If true, then all traffic redirection setup uses the consul-cni plugin. + # Requires connectInject.enabled to also be true. + # @type: boolean + enabled: false + + # Log level for the installer and plugin. Overrides global.logLevel + # @type: string + logLevel: null + + # Set the namespace to install the CNI plugin into. Overrides global namespace settings for CNI resources. + # Ex: "kube-system" + # @type: string + namespace: null + + # Location on the kubernetes node where the CNI plugin is installed. Shoud be the absolute path and start with a '/' + # Example on GKE: + # + # ```yaml + # cniBinDir: "/home/kubernetes/bin" + # ``` + # @type: string + cniBinDir: "/opt/cni/bin" + + # Location on the kubernetes node of all CNI configuration. Should be the absolute path and start with a '/' + # @type: string + cniNetDir: "/etc/cni/net.d" + + # If multus CNI plugin is enabled with consul-cni. When enabled, consul-cni will not be installed as a chained + # CNI plugin. Instead, a NetworkAttachementDefinition CustomResourceDefinition (CRD) will be created in the helm + # release namespace. Following multus plugin standards, an annotation is required in order for the consul-cni plugin + # to be executed and for your service to be added to the Consul Service Mesh. + # + # Add the annotation `'k8s.v1.cni.cncf.io/networks': '[{ "name":"consul-cni","namespace": "consul" }]'` to your pod + # to use the default installed NetworkAttachementDefinition CRD. + # + # Please refer to the [Multus Quickstart Guide](https://github.com/k8snetworkplumbingwg/multus-cni/blob/master/docs/quickstart.md) + # for more information about using multus. + # @type: string + multus: false + + # The resource settings for CNI installer daemonset. + # @recurse: false + # @type: map + resources: + requests: + memory: "75Mi" + cpu: "75m" + limits: + memory: "100Mi" + cpu: "100m" + + # Resource quotas for running the daemonset as system critical pods + resourceQuota: + pods: 5000 + + # The security context for the CNI installer daemonset. This should be a YAML map corresponding to a + # Kubernetes [SecurityContext](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/) object. + # By default, servers will run as root, with user ID `0` and group ID `0`. + # Note: if running on OpenShift, this setting is ignored because the user and group are set automatically + # by the OpenShift platform. + # @type: map + # @recurse: false + securityContext: + runAsNonRoot: false + runAsGroup: 0 + runAsUser: 0 + + # updateStrategy for the CNI installer DaemonSet. + # Refer to the Kubernetes [Daemonset upgrade strategy](https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/#daemonset-update-strategy) + # documentation. + # This should be a multi-line string mapping directly to the updateStrategy + # + # Example: + # + # ```yaml + # updateStrategy: | + # rollingUpdate: + # maxUnavailable: 5 + # type: RollingUpdate + # ``` + # + # @type: string + updateStrategy: null + + consulNode: + # meta specifies an arbitrary metadata key/value pair to associate with the node. + # + # Example: + # + # ```yaml + # meta: + # cluster: test-cluster + # persistent: true + # ``` + # + # @type: map + meta: null + + # Configures metrics for Consul service mesh services. All values are overridable + # via annotations on a per-pod basis. + metrics: + # If true, the connect-injector will automatically + # add prometheus annotations to connect-injected pods. It will also + # add a listener on the Envoy sidecar to expose metrics. The exposed + # metrics will depend on whether metrics merging is enabled: + # - If metrics merging is enabled: + # the consul-dataplane will run a merged metrics server + # combining Envoy sidecar and Connect service metrics, + # i.e. if your service exposes its own Prometheus metrics. + # - If metrics merging is disabled: + # the listener will just expose Envoy sidecar metrics. + # This will inherit from `global.metrics.enabled`. + defaultEnabled: "-" + # Configures the consul-dataplane to run a merged metrics server + # to combine and serve both Envoy and Connect service metrics. + # This feature is available only in Consul v1.10.0 or greater. + defaultEnableMerging: false + # Configures the port at which the consul-dataplane will listen on to return + # combined metrics. This port only needs to be changed if it conflicts with + # the application's ports. + defaultMergedMetricsPort: 20100 + # Configures the port Prometheus will scrape metrics from, by configuring + # the Pod annotation `prometheus.io/port` and the corresponding listener in + # the Envoy sidecar. + # NOTE: This is *not* the port that your application exposes metrics on. + # That can be configured with the + # `consul.hashicorp.com/service-metrics-port` annotation. + defaultPrometheusScrapePort: 20200 + # Configures the path Prometheus will scrape metrics from, by configuring the pod + # annotation `prometheus.io/path` and the corresponding handler in the Envoy + # sidecar. + # NOTE: This is *not* the path that your application exposes metrics on. + # That can be configured with the + # `consul.hashicorp.com/service-metrics-path` annotation. + defaultPrometheusScrapePath: "/metrics" + + # Used to pass arguments to the injected envoy sidecar. + # Valid arguments to pass to envoy can be found here: https://www.envoyproxy.io/docs/envoy/latest/operations/cli + # e.g "--log-level debug --disable-hot-restart" + # @type: string + envoyExtraArgs: null + + # Optional priorityClassName. + priorityClassName: "" + + # Extra labels to attach to the connect inject pods. This should be a YAML map. + # + # Example: + # + # ```yaml + # extraLabels: + # labelKey: label-value + # anotherLabelKey: another-label-value + # ``` + # + # @type: map + extraLabels: null + + # This value defines additional annotations for + # connect inject pods. This should be formatted as a multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + # The Docker image for Consul to use when performing Connect injection. + # Defaults to global.image. + # @type: string + imageConsul: null + + # Sets the `logLevel` for the `consul-dataplane` sidecar and the `consul-connect-inject-init` container. When set, this value overrides the global log verbosity level. One of "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + + serviceAccount: + # This value defines additional annotations for the injector service account. This should be formatted as a + # multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + # The resource settings for connect inject pods. The defaults, are optimized for getting started worklows on developer deployments. The settings should be tweaked for production deployments. + # @type: map + resources: + requests: + # Recommended production default: 500Mi + # @type: string + memory: "200Mi" + # Recommended production default: 250m + # @type: string + cpu: "50m" + limits: + # Recommended production default: 500Mi + # @type: string + memory: "200Mi" + # Recommended production default: 250m + # @type: string + cpu: "50m" + + # Sets the failurePolicy for the mutating webhook. By default this will cause pods not part of the consul installation to fail scheduling while the webhook + # is offline. This prevents a pod from skipping mutation if the webhook were to be momentarily offline. + # Once the webhook is back online the pod will be scheduled. + # In some environments such as Kind this may have an undesirable effect as it may prevent volume provisioner pods from running + # which can lead to hangs. In these environments it is recommend to use "Ignore" instead. + # This setting can be safely disabled by setting to "Ignore". + failurePolicy: "Fail" + + # Selector for restricting the webhook to only specific namespaces. + # Use with `connectInject.default: true` to automatically inject all pods in namespaces that match the selector. This should be set to a multiline string. + # Refer to https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector + # for more details. + # + # By default, we exclude kube-system since usually users won't + # want those pods injected and local-path-storage and openebs so that + # Kind (Kubernetes In Docker) and [OpenEBS](https://openebs.io/) respectively can provision Pods used to create PVCs. + # Note that this exclusion is only supported in Kubernetes v1.21.1+. + # + # Example: + # + # ```yaml + # namespaceSelector: | + # matchLabels: + # namespace-label: label-value + # ``` + # @type: string + namespaceSelector: | + matchExpressions: + - key: "kubernetes.io/metadata.name" + operator: "NotIn" + values: ["kube-system","local-path-storage","openebs"] + + # List of k8s namespaces to allow Connect sidecar + # injection in. If a k8s namespace is not included or is listed in `k8sDenyNamespaces`, + # pods in that k8s namespace will not be injected even if they are explicitly + # annotated. Use `["*"]` to automatically allow all k8s namespaces. + # + # For example, `["namespace1", "namespace2"]` will only allow pods in the k8s + # namespaces `namespace1` and `namespace2` to have Consul service mesh sidecars injected + # and registered with Consul. All other k8s namespaces will be ignored. + # + # To deny all namespaces, set this to `[]`. + # + # Note: `k8sDenyNamespaces` takes precedence over values defined here and + # `namespaceSelector` takes precedence over both since it is applied first. + # `kube-system` and `kube-public` are never injected, even if included here. + # @type: array + k8sAllowNamespaces: ["*"] + + # List of k8s namespaces that should not allow Connect + # sidecar injection. This list takes precedence over `k8sAllowNamespaces`. + # `*` is not supported because then nothing would be allowed to be injected. + # + # For example, if `k8sAllowNamespaces` is `["*"]` and k8sDenyNamespaces is + # `["namespace1", "namespace2"]`, then all k8s namespaces besides "namespace1" + # and "namespace2" will be available for injection. + # + # Note: `namespaceSelector` takes precedence over this since it is applied first. + # `kube-system` and `kube-public` are never injected. + # @type: array + k8sDenyNamespaces: [] + + # [Enterprise Only] These settings manage the connect injector's interaction with + # Consul namespaces (requires consul-ent v1.7+). + # Also, `global.enableConsulNamespaces` must be true. + consulNamespaces: + # Name of the Consul namespace to register all + # k8s pods into. If the Consul namespace does not already exist, + # it will be created. This will be ignored if `mirroringK8S` is true. + consulDestinationNamespace: "default" + + # Causes k8s pods to be registered into a Consul namespace + # of the same name as their k8s namespace, optionally prefixed if + # `mirroringK8SPrefix` is set below. If the Consul namespace does not + # already exist, it will be created. Turning this on overrides the + # `consulDestinationNamespace` setting. If mirroring is enabled, avoid creating any Consul + # resources in the following Kubernetes namespaces, as Consul currently reserves these + # namespaces for system use: "system", "universal", "operator", "root". + mirroringK8S: true + + # If `mirroringK8S` is set to true, `mirroringK8SPrefix` allows each Consul namespace + # to be given a prefix. For example, if `mirroringK8SPrefix` is set to "k8s-", a + # pod in the k8s `staging` namespace will be registered into the + # `k8s-staging` Consul namespace. + mirroringK8SPrefix: "" + + # Selector labels for connectInject pod assignment, formatted as a multi-line string. + # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + # + # Example: + # + # ```yaml + # nodeSelector: | + # beta.kubernetes.io/arch: amd64 + # ``` + # @type: string + nodeSelector: null + + # Affinity Settings + # This should be a multi-line string matching the affinity object + # @type: string + affinity: null + + # Toleration Settings + # This should be a multi-line string matching the Toleration array + # in a PodSpec. + # @type: string + tolerations: null + + # Query that defines which Service Accounts + # can authenticate to Consul and receive an ACL token during Connect injection. + # The default setting, i.e. serviceaccount.name!=default, prevents the + # 'default' Service Account from logging in. + # If set to an empty string all service accounts can log in. + # This only has effect if ACLs are enabled. + # + # Refer to Auth methods [Binding rules](https://developer.hashicorp.com/consul/docs/security/acl/auth-methods#binding-rules) + # and [Trusted identiy attributes](https://developer.hashicorp.com/consul/docs/security/acl/auth-methods/kubernetes#trusted-identity-attributes) + # for more details. + # Requires Consul >= v1.5. + aclBindingRuleSelector: "serviceaccount.name!=default" + + # If you are not using global.acls.manageSystemACLs and instead manually setting up an + # auth method for Connect inject, set this to the name of your auth method. + overrideAuthMethodName: "" + + # Refers to a Kubernetes secret that you have created that contains + # an ACL token for your Consul cluster which allows the Connect injector the correct + # permissions. This is only needed if Consul namespaces [Enterprise Only] and ACLs + # are enabled on the Consul cluster and you are not setting + # `global.acls.manageSystemACLs` to `true`. + # This token needs to have `operator = "write"` privileges to be able to + # create Consul namespaces. + aclInjectToken: + # The name of the Vault secret that holds the ACL inject token. + # @type: string + secretName: null + # The key within the Vault secret that holds the ACL inject token. + # @type: string + secretKey: null + + sidecarProxy: + # The number of worker threads to be used by the Envoy proxy. + # By default the threading model of Envoy will use one thread per CPU core per envoy proxy. This + # leads to unnecessary thread and memory usage and leaves unnecessary idle connections open. It is + # advised to keep this number low for sidecars and high for edge proxies. + # This will control the `--concurrency` flag to Envoy. + # For additional information, refer to https://blog.envoyproxy.io/envoy-threading-model-a8d44b922310 + # + # This setting can be overridden on a per-pod basis via this annotation: + # - `consul.hashicorp.com/consul-envoy-proxy-concurrency` + # @type: string + concurrency: 2 + + # Set default resources for sidecar proxy. If null, that resource won't + # be set. + # These settings can be overridden on a per-pod basis via these annotations: + # + # - `consul.hashicorp.com/sidecar-proxy-cpu-limit` + # - `consul.hashicorp.com/sidecar-proxy-cpu-request` + # - `consul.hashicorp.com/sidecar-proxy-memory-limit` + # - `consul.hashicorp.com/sidecar-proxy-memory-request` + # @type: map + resources: + requests: + # Recommended production default: 100Mi + # @type: string + memory: null + # Recommended production default: 100m + # @type: string + cpu: null + limits: + # Recommended production default: 100Mi + # @type: string + memory: null + # Recommended production default: 100m + # @type: string + cpu: null + # Set default lifecycle management configuration for sidecar proxy. + # These settings can be overridden on a per-pod basis via these annotations: + # + # - `consul.hashicorp.com/enable-sidecar-proxy-lifecycle` + # - `consul.hashicorp.com/enable-sidecar-proxy-shutdown-drain-listeners` + # - `consul.hashicorp.com/sidecar-proxy-lifecycle-shutdown-grace-period-seconds` + # - `consul.hashicorp.com/sidecar-proxy-lifecycle-startup-grace-period-seconds` + # - `consul.hashicorp.com/sidecar-proxy-lifecycle-graceful-port` + # - `consul.hashicorp.com/sidecar-proxy-lifecycle-graceful-shutdown-path` + # - `consul.hashicorp.com/sidecar-proxy-lifecycle-graceful-startup-path` + # @type: map + lifecycle: + # @type: boolean + defaultEnabled: true + # @type: boolean + defaultEnableShutdownDrainListeners: true + # @type: integer + defaultShutdownGracePeriodSeconds: 30 + # @type: integer + defaultStartupGracePeriodSeconds: 0 + # @type: integer + defaultGracefulPort: 20600 + # @type: string + defaultGracefulShutdownPath: "/graceful_shutdown" + # @type: string + defaultGracefulStartupPath: "/graceful_startup" + + # Configures how long the k8s startup probe will wait before the proxy is considered to be unhealthy and the container is restarted. + # A value of zero disables the probe. + defaultStartupFailureSeconds: 0 + # Configures how long the k8s liveness probe will wait before the proxy is considered to be unhealthy and the container is restarted. + # A value of zero disables the probe. + defaultLivenessFailureSeconds: 0 + + # The resource settings for the Connect injected init container. If null, the resources + # won't be set for the initContainer. The defaults are optimized for developer instances of + # Kubernetes, however they should be tweaked with the recommended defaults as shown below to speed up service registration times. + # @type: map + initContainer: + resources: + requests: + # Recommended production default: 150Mi + # @type: string + memory: "25Mi" + # Recommended production default: 250m + # @type: string + cpu: "50m" + limits: + # Recommended production default: 150Mi + # @type: string + memory: "150Mi" + # Recommended production default: 500m + # @type: string + cpu: null + +# [Mesh Gateways](https://developer.hashicorp.com/consul/docs/connect/gateways/mesh-gateway) enable Consul Connect to work across Consul datacenters. +meshGateway: + # If [mesh gateways](https://developer.hashicorp.com/consul/docs/connect/gateways/mesh-gateway) are enabled, a Deployment will be created that runs + # gateways and Consul service mesh will be configured to use gateways. + # This setting is required for [Cluster Peering](https://developer.hashicorp.com/consul/docs/connect/cluster-peering/k8s). + # Requirements: consul 1.6.0+ if using `global.acls.manageSystemACLs``. + enabled: false + + # Override global log verbosity level for mesh-gateway-deployment pods. One of "trace", "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + + # Number of replicas for the Deployment. + replicas: 1 + + # What gets registered as WAN address for the gateway. + wanAddress: + # source configures where to retrieve the WAN address (and possibly port) + # for the mesh gateway from. + # Can be set to either: `Service`, `NodeIP`, `NodeName` or `Static`. + # + # - `Service` - Determine the address based on the service type. + # + # - If `service.type=LoadBalancer` use the external IP or hostname of + # the service. Use the port set by `service.port`. + # + # - If `service.type=NodePort` use the Node IP. The port will be set to + # `service.nodePort` so `service.nodePort` cannot be null. + # + # - If `service.type=ClusterIP` use the `ClusterIP`. The port will be set to + # `service.port`. + # + # - `service.type=ExternalName` is not supported. + # + # - `NodeIP` - The node IP as provided by the Kubernetes downward API. + # + # - `NodeName` - The name of the node as provided by the Kubernetes downward + # API. This is useful if the node names are DNS entries that + # are routable from other datacenters. + # + # - `Static` - Use the address hardcoded in `meshGateway.wanAddress.static`. + source: Service + + # Port that gets registered for WAN traffic. + # If source is set to "Service" then this setting will have no effect. + # Refer to the documentation for source as to which port will be used in that + # case. + port: 443 + + # If source is set to "Static" then this value will be used as the WAN + # address of the mesh gateways. This is useful if you've configured a + # DNS entry to point to your mesh gateways. + static: "" + + # The service option configures the Service that fronts the Gateway Deployment. + service: + # Type of service, ex. LoadBalancer, ClusterIP. + type: LoadBalancer + + # Port that the service will be exposed on. + # The targetPort will be set to meshGateway.containerPort. + port: 443 + + # Optionally set the nodePort value of the service if using a NodePort service. + # If not set and using a NodePort service, Kubernetes will automatically assign + # a port. + # @type: integer + nodePort: null + + # Annotations to apply to the mesh gateway service. + # + # Example: + # + # ```yaml + # annotations: | + # 'annotation-key': annotation-value + # ``` + # @type: string + annotations: null + + # Optional YAML string that will be appended to the Service spec. + # @type: string + additionalSpec: null + + # If set to true, gateway Pods will run on the host network. + hostNetwork: false + + # dnsPolicy to use. + # @type: string + dnsPolicy: null + + # Consul service name for the mesh gateways. + # Cannot be set to anything other than "mesh-gateway" if + # global.acls.manageSystemACLs is true since the ACL token + # generated is only for the name 'mesh-gateway'. + consulServiceName: "mesh-gateway" + + # Port that the gateway will run on inside the container. + containerPort: 8443 + + # Optional hostPort for the gateway to be exposed on. + # This can be used with wanAddress.port and wanAddress.useNodeIP + # to expose the gateways directly from the node. + # If hostNetwork is true, this must be null or set to the same port as + # containerPort. + # NOTE: Cannot set to 8500 or 8502 because those are reserved for the Consul + # agent. + # @type: integer + hostPort: null + + serviceAccount: + # This value defines additional annotations for the mesh gateways' service account. This should be formatted as a + # multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + # The resource settings for mesh gateway pods. + # NOTE: The use of a YAML string is deprecated. Instead, set directly as a + # YAML map. + # @recurse: false + # @type: map + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "100Mi" + cpu: "100m" + + # The resource settings for the `service-init` init container. + # @recurse: false + # @type: map + initServiceInitContainer: + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" + + # This value defines the [affinity](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity) + # for mesh gateway pods. It defaults to `null` thereby allowing multiple gateway pods on each node. But if one would prefer + # a mode which minimizes risk of the cluster becoming unusable if a node is lost, set this value + # to the value in the example below. + # + # Example: + # + # ```yaml + # affinity: | + # podAntiAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # - labelSelector: + # matchLabels: + # app: {{ template "consul.name" . }} + # release: "{{ .Release.Name }}" + # component: mesh-gateway + # topologyKey: kubernetes.io/hostname + # ``` + # @type: string + affinity: null + + # Optional YAML string to specify tolerations. + # @type: string + tolerations: null + + # Pod topology spread constraints for mesh gateway pods. + # This should be a multi-line YAML string matching the + # [`topologySpreadConstraints`](https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/) + # array in a Pod Spec. + # + # This requires K8S >= 1.18 (beta) or 1.19 (stable). + # + # Example: + # + # ```yaml + # topologySpreadConstraints: | + # - maxSkew: 1 + # topologyKey: topology.kubernetes.io/zone + # whenUnsatisfiable: DoNotSchedule + # labelSelector: + # matchLabels: + # app: {{ template "consul.name" . }} + # release: "{{ .Release.Name }}" + # component: mesh-gateway + # ``` + topologySpreadConstraints: "" + + # Optional YAML string to specify a nodeSelector config. + # @type: string + nodeSelector: null + + # Optional priorityClassName. + priorityClassName: "" + + # Annotations to apply to the mesh gateway deployment. + # + # Example: + # + # ```yaml + # annotations: | + # 'annotation-key': annotation-value + # ``` + # @type: string + annotations: null + +# Configuration options for ingress gateways. Default values for all +# ingress gateways are defined in `ingressGateways.defaults`. Any of +# these values may be overridden in `ingressGateways.gateways` for a +# specific gateway with the exception of annotations. Annotations will +# include both the default annotations and any additional ones defined +# for a specific gateway. +# Requirements: consul >= 1.8.0 +ingressGateways: + # Enable ingress gateway deployment. Requires `connectInject.enabled=true`. + enabled: false + + # Override global log verbosity level for ingress-gateways-deployment pods. One of "trace", "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + + # Defaults sets default values for all gateway fields. With the exception + # of annotations, defining any of these values in the `gateways` list + # will override the default values provided here. Annotations will + # include both the default annotations and any additional ones defined + # for a specific gateway. + defaults: + # Number of replicas for each ingress gateway defined. + replicas: 1 + + # The service options configure the Service that fronts the gateway Deployment. + service: + # Type of service: LoadBalancer, ClusterIP or NodePort. If using NodePort service + # type, you must set the desired nodePorts in the `ports` setting below. + type: ClusterIP + + # Ports that will be exposed on the service and gateway container. Any + # ports defined as ingress listeners on the gateway's Consul configuration + # entry should be included here. The first port will be used as part of + # the Consul service registration for the gateway and be listed in its + # SRV record. If using a NodePort service type, you must specify the + # desired nodePort for each exposed port. + # @type: array + # @default: [{port: 8080, port: 8443}] + # @recurse: false + ports: + - port: 8080 + nodePort: null + - port: 8443 + nodePort: null + + # Annotations to apply to the ingress gateway service. Annotations defined + # here will be applied to all ingress gateway services in addition to any + # service annotations defined for a specific gateway in `ingressGateways.gateways`. + # + # Example: + # + # ```yaml + # annotations: | + # 'annotation-key': annotation-value + # ``` + # @type: string + annotations: null + + # Optional YAML string that will be appended to the Service spec. + # @type: string + additionalSpec: null + + serviceAccount: + # This value defines additional annotations for the ingress gateways' service account. This should be formatted + # as a multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + # Resource limits for all ingress gateway pods + # @recurse: false + # @type: map + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "100Mi" + cpu: "100m" + + # This value defines the [affinity](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity) + # for ingress gateway pods. It defaults to `null` thereby allowing multiple gateway pods on each node. But if one would prefer + # a mode which minimizes risk of the cluster becoming unusable if a node is lost, set this value + # to the value in the example below. + # + # Example: + # + # ```yaml + # affinity: | + # podAntiAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # - labelSelector: + # matchLabels: + # app: {{ template "consul.name" . }} + # release: "{{ .Release.Name }}" + # component: ingress-gateway + # topologyKey: kubernetes.io/hostname + # ``` + # @type: string + affinity: null + + # Optional YAML string to specify tolerations. + # @type: string + tolerations: null + + # Pod topology spread constraints for ingress gateway pods. + # This should be a multi-line YAML string matching the + # [`topologySpreadConstraints`](https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/) + # array in a Pod Spec. + # + # This requires K8S >= 1.18 (beta) or 1.19 (stable). + # + # Example: + # + # ```yaml + # topologySpreadConstraints: | + # - maxSkew: 1 + # topologyKey: topology.kubernetes.io/zone + # whenUnsatisfiable: DoNotSchedule + # labelSelector: + # matchLabels: + # app: {{ template "consul.name" . }} + # release: "{{ .Release.Name }}" + # component: ingress-gateway + # ``` + topologySpreadConstraints: "" + + # Optional YAML string to specify a nodeSelector config. + # @type: string + nodeSelector: null + + # Optional priorityClassName. + priorityClassName: "" + + # Amount of seconds to wait for graceful termination before killing the pod. + terminationGracePeriodSeconds: 10 + + # Annotations to apply to the ingress gateway deployment. Annotations defined + # here will be applied to all ingress gateway deployments in addition to any + # annotations defined for a specific gateway in `ingressGateways.gateways`. + # + # Example: + # + # ```yaml + # annotations: | + # "annotation-key": 'annotation-value' + # ``` + # @type: string + annotations: null + + # [Enterprise Only] `consulNamespace` defines the Consul namespace to register + # the gateway into. Requires `global.enableConsulNamespaces` to be true and + # Consul Enterprise v1.7+ with a valid Consul Enterprise license. + # Note: The Consul namespace MUST exist before the gateway is deployed. + consulNamespace: "default" + + # Gateways is a list of gateway objects. The only required field for + # each is `name`, though they can also contain any of the fields in + # `defaults`. You must provide a unique name for each ingress gateway. These names + # must be unique across different namespaces. + # Values defined here override the defaults, except in the case of annotations where both will be applied. + # @type: array + gateways: + - name: ingress-gateway + +# Configuration options for terminating gateways. Default values for all +# terminating gateways are defined in `terminatingGateways.defaults`. Any of +# these values may be overridden in `terminatingGateways.gateways` for a +# specific gateway with the exception of annotations. Annotations will +# include both the default annotations and any additional ones defined +# for a specific gateway. +# Requirements: consul >= 1.8.0 +terminatingGateways: + # Enable terminating gateway deployment. Requires `connectInject.enabled=true`. + enabled: false + + # Override global log verbosity level. One of "trace", "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + + # Defaults sets default values for all gateway fields. With the exception + # of annotations, defining any of these values in the `gateways` list + # will override the default values provided here. Annotations will + # include both the default annotations and any additional ones defined + # for a specific gateway. + defaults: + # Number of replicas for each terminating gateway defined. + replicas: 1 + + # A list of extra volumes to mount. These will be exposed to Consul in the path `/consul/userconfig//`. + # + # Example: + # + # ```yaml + # extraVolumes: + # - type: secret + # name: my-secret + # items: # optional items array + # - key: key + # path: path # secret will now mount to /consul/userconfig/my-secret/path + # ``` + # @type: array + extraVolumes: [] + + # Resource limits for all terminating gateway pods + # @recurse: false + # @type: map + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "100Mi" + cpu: "100m" + + # This value defines the [affinity](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity) + # for terminating gateway pods. It defaults to `null` thereby allowing multiple gateway pods on each node. But if one would prefer + # a mode which minimizes risk of the cluster becoming unusable if a node is lost, set this value + # to the value in the example below. + # + # Example: + # + # ```yaml + # affinity: | + # podAntiAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # - labelSelector: + # matchLabels: + # app: {{ template "consul.name" . }} + # release: "{{ .Release.Name }}" + # component: terminating-gateway + # topologyKey: kubernetes.io/hostname + # ``` + # @type: string + affinity: null + + # Optional YAML string to specify tolerations. + # @type: string + tolerations: null + + # Pod topology spread constraints for terminating gateway pods. + # This should be a multi-line YAML string matching the + # [`topologySpreadConstraints`](https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/) + # array in a Pod Spec. + # + # This requires K8S >= 1.18 (beta) or 1.19 (stable). + # + # Example: + # + # ```yaml + # topologySpreadConstraints: | + # - maxSkew: 1 + # topologyKey: topology.kubernetes.io/zone + # whenUnsatisfiable: DoNotSchedule + # labelSelector: + # matchLabels: + # app: {{ template "consul.name" . }} + # release: "{{ .Release.Name }}" + # component: terminating-gateway + # ``` + topologySpreadConstraints: "" + + # Optional YAML string to specify a nodeSelector config. + # @type: string + nodeSelector: null + + # Optional priorityClassName. + # @type: string + priorityClassName: "" + + # Annotations to apply to the terminating gateway deployment. Annotations defined + # here will be applied to all terminating gateway deployments in addition to any + # annotations defined for a specific gateway in `terminatingGateways.gateways`. + # + # Example: + # + # ```yaml + # annotations: | + # 'annotation-key': annotation-value + # ``` + # @type: string + annotations: null + + serviceAccount: + # This value defines additional annotations for the terminating gateways' service account. This should be + # formatted as a multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + # [Enterprise Only] `consulNamespace` defines the Consul namespace to register + # the gateway into. Requires `global.enableConsulNamespaces` to be true and + # Consul Enterprise v1.7+ with a valid Consul Enterprise license. + # Note: The Consul namespace MUST exist before the gateway is deployed. + consulNamespace: "default" + + # Gateways is a list of gateway objects. The only required field for + # each is `name`, though they can also contain any of the fields in + # `defaults`. Values defined here override the defaults except in the + # case of annotations where both will be applied. + # @type: array + gateways: + - name: terminating-gateway + +# Configuration settings for the webhook-cert-manager +# `webhook-cert-manager` ensures that cert bundles are up to date for the mutating webhook. +webhookCertManager: + # Toleration Settings + # This should be a multi-line string matching the Toleration array + # in a PodSpec. + # @type: string + tolerations: null + + # This value defines [`nodeSelector`](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) + # labels for the webhook-cert-manager pod assignment, formatted as a multi-line string. + # + # Example: + # + # ```yaml + # nodeSelector: | + # beta.kubernetes.io/arch: amd64 + # ``` + # + # @type: string + nodeSelector: null + + # The resource requests (CPU, memory, etc.) for the server-acl-init and server-acl-init-cleanup pods. + # This should be a YAML map corresponding to a Kubernetes + # [`ResourceRequirements``](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#resourcerequirements-v1-core) + # object. + # + # Example: + # + # ```yaml + # resources: + # requests: + # memory: '200Mi' + # cpu: '100m' + # limits: + # memory: '200Mi' + # cpu: '100m' + # ``` + # + # @recurse: false + # @type: map + resources: + requests: + memory: "50Mi" + cpu: "100m" + limits: + memory: "50Mi" + cpu: "100m" + +# Configures a demo Prometheus installation. +prometheus: + # When true, the Helm chart will install a demo Prometheus server instance + # alongside Consul. + enabled: false + +# Control whether a test Pod manifest is generated when running helm template. +# When using helm install, the test Pod is not submitted to the cluster so this +# is only useful when running helm template. +tests: + enabled: true + +telemetryCollector: + # Enables the consul-telemetry-collector deployment + # @type: boolean + enabled: false + + # Override global log verbosity level. One of "trace", "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + + # The name of the Docker image (including any tag) for the containers running + # the consul-telemetry-collector + # @type: string + image: "hashicorp/consul-telemetry-collector:0.0.2" + + # The resource settings for consul-telemetry-collector pods. + # @recurse: false + # @type: map + resources: + requests: + memory: "512Mi" + cpu: "1000m" + limits: + memory: "512Mi" + cpu: "1000m" + + # This value sets the number of consul-telemetry-collector replicas to deploy. + replicas: 1 + + # This value defines additional configuration for the telemetry collector. It should be formatted as a multi-line + # json blob string + # + # ```yaml + # customExporterConfig: | + # {"http_collector_endpoint": "other-otel-collector"} + # ``` + # + # @type: string + customExporterConfig: null + + service: + # This value defines additional annotations for the telemetry-collector's service account. This should be formatted as a multi-line + # string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + serviceAccount: + # This value defines additional annotations for the telemetry-collector's service account. This should be formatted + # as a multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + cloud: + # The resource id of the HCP Consul Central cluster to push metrics for. Eg: + # `organization/27109cd4-a309-4bf3-9986-e1d071914b18/project/fcef6c24-259d-4510-bb8d-1d812e120e34/hashicorp.consul.global-network-manager.cluster/consul-cluster` + # + # This is used for HCP Consul Central-linked or HCP Consul Dedicated clusters where global.cloud.resourceId is unset. For example, when using externalServers + # with HCP Consul Dedicated clusters or HCP Consul Central-linked clusters in a different admin partition. + # + # If global.cloud.resourceId is set, this should either be unset (defaulting to global.cloud.resourceId) or be the same as global.cloud.resourceId. + # + # @default: global.cloud.resourceId + resourceId: + # The name of the Kubernetes secret that holds the resource id. + # @type: string + secretName: null + # The key within the Kubernetes secret that holds the resource id. + # @type: string + secretKey: null + + # The client id portion of a [service principal](https://developer.hashicorp.com/hcp/docs/hcp/admin/iam/service-principals#service-principals) with authorization to push metrics to HCP + # + # This is set in two scenarios: + # - the service principal in global.cloud is unset + # - the HCP UI provides a service principal with more narrowly scoped permissions that the service principal used in global.cloud + # + # @default: global.cloud.clientId + clientId: + # The name of the Kubernetes secret that holds the client id. + # @type: string + secretName: null + # The key within the Kubernetes secret that holds the client id. + # @type: string + secretKey: null + + # The client secret portion of a [service principal](https://developer.hashicorp.com/hcp/docs/hcp/admin/iam/service-principals#service-principals) with authorization to push metrics to HCP. + # + # This is set in two scenarios: + # - the service principal in global.cloud is unset + # - the HCP UI provides a service principal with more narrowly scoped permissions that the service principal used in global.cloud + # + # @default: global.cloud.clientSecret + clientSecret: + # The name of the Kubernetes secret that holds the client secret. + # @type: string + secretName: null + # The key within the Kubernetes secret that holds the client secret. + # @type: string + secretKey: null + + initContainer: + # The resource settings for consul-telemetry-collector initContainer. + # @recurse: false + # @type: map + resources: {} + + # Optional YAML string to specify a nodeSelector config. + # @type: string + nodeSelector: null + + # Optional priorityClassName. + # @type: string + priorityClassName: "" + + # A list of extra environment variables to set within the deployment. + # These could be used to include proxy settings required for cloud auto-join + # feature, in case kubernetes cluster is behind egress http proxies. Additionally, + # it could be used to configure custom consul parameters. + # @type: map + extraEnvironmentVars: {} diff --git a/charts/kuma/kuma/2.8.3/.helmdocsignore b/charts/kuma/kuma/2.8.3/.helmdocsignore new file mode 100644 index 0000000000..d8a5db8f87 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/.helmdocsignore @@ -0,0 +1 @@ +# Charts to ignore from helm-docs \ No newline at end of file diff --git a/charts/kuma/kuma/2.8.3/.helmignore b/charts/kuma/kuma/2.8.3/.helmignore new file mode 100644 index 0000000000..0e8a0eb36f --- /dev/null +++ b/charts/kuma/kuma/2.8.3/.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 +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/kuma/kuma/2.8.3/Chart.yaml b/charts/kuma/kuma/2.8.3/Chart.yaml new file mode 100644 index 0000000000..7f31b70f69 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/Chart.yaml @@ -0,0 +1,26 @@ +annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Kuma + catalog.cattle.io/namespace: kuma-system + catalog.cattle.io/release-name: kuma +apiVersion: v2 +appVersion: 2.8.3 +description: A Helm chart for the Kuma Control Plane +home: https://github.com/kumahq/kuma +icon: file://assets/icons/kuma.svg +keywords: +- service mesh +- control plane +maintainers: +- email: jakub.dyszkiewicz@konghq.com + name: Jakub Dyszkiewicz + url: https://github.com/jakubdyszkiewicz +- email: charly.molter@konghq.com + name: Charly Molter + url: https://github.com/lahabana +- email: michael.beaumont@konghq.com + name: Mike Beaumont + url: https://github.com/michaelbeaumont +name: kuma +type: application +version: 2.8.3 diff --git a/charts/kuma/kuma/2.8.3/README.md b/charts/kuma/kuma/2.8.3/README.md new file mode 100644 index 0000000000..42df4d96e7 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/README.md @@ -0,0 +1,256 @@ +[![][kuma-logo]][kuma-url] + +A Helm chart for the Kuma Control Plane + +![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![Version: 2.8.3](https://img.shields.io/badge/Version-2.8.3-informational?style=flat-square) ![AppVersion: 2.8.3](https://img.shields.io/badge/AppVersion-2.8.3-informational?style=flat-square) + +**Homepage:** + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| global.image.registry | string | `"docker.io/kumahq"` | Default registry for all Kuma Images | +| global.image.tag | string | `nil` | The default tag for all Kuma images, which itself defaults to .Chart.AppVersion | +| global.imagePullSecrets | list | `[]` | Add `imagePullSecrets` to all the service accounts used for Kuma components | +| patchSystemNamespace | bool | `true` | Whether to patch the target namespace with the system label | +| installCrdsOnUpgrade.enabled | bool | `true` | Whether install new CRDs before upgrade (if any were introduced with the new version of Kuma) | +| installCrdsOnUpgrade.imagePullSecrets | list | `[]` | The `imagePullSecrets` to attach to the Service Account running CRD installation. This field will be deprecated in a future release, please use .global.imagePullSecrets | +| noHelmHooks | bool | `false` | Whether to disable all helm hooks | +| restartOnSecretChange | bool | `true` | Whether to restart control-plane by calculating a new checksum for the secret | +| controlPlane.environment | string | `"kubernetes"` | Environment that control plane is run in, useful when running universal global control plane on k8s | +| controlPlane.extraLabels | object | `{}` | Labels to add to resources in addition to default labels | +| controlPlane.logLevel | string | `"info"` | Kuma CP log level: one of off,info,debug | +| controlPlane.logOutputPath | string | `""` | Kuma CP log output path: Defaults to /dev/stdout | +| controlPlane.mode | string | `"zone"` | Kuma CP modes: one of zone,global | +| controlPlane.zone | string | `nil` | Kuma CP zone, if running multizone | +| controlPlane.kdsGlobalAddress | string | `""` | Only used in `zone` mode | +| controlPlane.replicas | int | `1` | Number of replicas of the Kuma CP. Ignored when autoscaling is enabled | +| controlPlane.minReadySeconds | int | `0` | Minimum number of seconds for which a newly created pod should be ready for it to be considered available. | +| controlPlane.deploymentAnnotations | object | `{}` | Annotations applied only to the `Deployment` resource | +| controlPlane.podAnnotations | object | `{}` | Annotations applied only to the `Pod` resource | +| controlPlane.autoscaling.enabled | bool | `false` | Whether to enable Horizontal Pod Autoscaling, which requires the [Metrics Server](https://github.com/kubernetes-sigs/metrics-server) in the cluster | +| controlPlane.autoscaling.minReplicas | int | `2` | The minimum CP pods to allow | +| controlPlane.autoscaling.maxReplicas | int | `5` | The max CP pods to scale to | +| controlPlane.autoscaling.targetCPUUtilizationPercentage | int | `80` | For clusters that don't support autoscaling/v2, autoscaling/v1 is used | +| controlPlane.autoscaling.metrics | list | `[{"resource":{"name":"cpu","target":{"averageUtilization":80,"type":"Utilization"}},"type":"Resource"}]` | For clusters that do support autoscaling/v2, use metrics | +| controlPlane.nodeSelector | object | `{"kubernetes.io/os":"linux"}` | Node selector for the Kuma Control Plane pods | +| controlPlane.tolerations | list | `[]` | Tolerations for the Kuma Control Plane pods | +| controlPlane.podDisruptionBudget.enabled | bool | `false` | Whether to create a pod disruption budget | +| controlPlane.podDisruptionBudget.maxUnavailable | int | `1` | The maximum number of unavailable pods allowed by the budget | +| controlPlane.affinity | object | `{"podAntiAffinity":{"preferredDuringSchedulingIgnoredDuringExecution":[{"podAffinityTerm":{"labelSelector":{"matchExpressions":[{"key":"app.kubernetes.io/name","operator":"In","values":["{{ include \"kuma.name\" . }}"]},{"key":"app.kubernetes.io/instance","operator":"In","values":["{{ .Release.Name }}"]},{"key":"app","operator":"In","values":["{{ include \"kuma.name\" . }}-control-plane"]}]},"topologyKey":"kubernetes.io/hostname"},"weight":100}]}}` | Affinity placement rule for the Kuma Control Plane pods. This is rendered as a template, so you can reference other helm variables or includes. | +| controlPlane.topologySpreadConstraints | string | `nil` | Topology spread constraints rule for the Kuma Control Plane pods. This is rendered as a template, so you can use variables to generate match labels. | +| controlPlane.injectorFailurePolicy | string | `"Fail"` | Failure policy of the mutating webhook implemented by the Kuma Injector component | +| controlPlane.service.apiServer.http.nodePort | int | `30681` | Port on which Http api server Service is exposed on Node for service of type NodePort | +| controlPlane.service.apiServer.https.nodePort | int | `30682` | Port on which Https api server Service is exposed on Node for service of type NodePort | +| controlPlane.service.enabled | bool | `true` | Whether to create a service resource. | +| controlPlane.service.name | string | `nil` | Optionally override of the Kuma Control Plane Service's name | +| controlPlane.service.type | string | `"ClusterIP"` | Service type of the Kuma Control Plane | +| controlPlane.service.annotations | object | `{"prometheus.io/port":"5680","prometheus.io/scrape":"true"}` | Annotations to put on the Kuma Control Plane | +| controlPlane.ingress.enabled | bool | `false` | Install K8s Ingress resource that exposes GUI and API | +| controlPlane.ingress.ingressClassName | string | `nil` | IngressClass defines which controller will implement the resource | +| controlPlane.ingress.hostname | string | `nil` | Ingress hostname | +| controlPlane.ingress.annotations | object | `{}` | Map of ingress annotations. | +| controlPlane.ingress.path | string | `"/"` | Ingress path. | +| controlPlane.ingress.pathType | string | `"ImplementationSpecific"` | Each path in an Ingress is required to have a corresponding path type. (ImplementationSpecific/Exact/Prefix) | +| controlPlane.ingress.servicePort | int | `5681` | Port from kuma-cp to use to expose API and GUI. Switch to 5682 to expose TLS port | +| controlPlane.globalZoneSyncService.enabled | bool | `true` | Whether to create a k8s service for the global zone sync service. It will only be created when enabled and deploying the global control plane. | +| controlPlane.globalZoneSyncService.type | string | `"LoadBalancer"` | Service type of the Global-zone sync | +| controlPlane.globalZoneSyncService.loadBalancerIP | string | `nil` | Optionally specify IP to be used by cloud provider when configuring load balancer | +| controlPlane.globalZoneSyncService.loadBalancerSourceRanges | list | `[]` | Optionally specify allowed source ranges that can access the load balancer | +| controlPlane.globalZoneSyncService.annotations | object | `{}` | Additional annotations to put on the Global Zone Sync Service | +| controlPlane.globalZoneSyncService.nodePort | int | `30685` | Port on which Global Zone Sync Service is exposed on Node for service of type NodePort | +| controlPlane.globalZoneSyncService.port | int | `5685` | Port on which Global Zone Sync Service is exposed | +| controlPlane.globalZoneSyncService.protocol | string | `"grpc"` | Protocol of the Global Zone Sync service port | +| controlPlane.defaults.skipMeshCreation | bool | `false` | Whether to skip creating the default Mesh | +| controlPlane.automountServiceAccountToken | bool | `true` | Whether to automountServiceAccountToken for cp. Optionally set to false | +| controlPlane.resources | object | `{"limits":{"memory":"256Mi"},"requests":{"cpu":"500m","memory":"256Mi"}}` | Optionally override the resource spec | +| controlPlane.lifecycle | object | `{}` | Pod lifecycle settings (useful for adding a preStop hook, when using AWS ALB or NLB) | +| controlPlane.terminationGracePeriodSeconds | int | `30` | Number of seconds to wait before force killing the pod. Make sure to update this if you add a preStop hook. | +| controlPlane.tls.general.secretName | string | `""` | Secret that contains tls.crt, tls.key [and ca.crt when no controlPlane.tls.general.caSecretName specified] for protecting Kuma in-cluster communication | +| controlPlane.tls.general.caSecretName | string | `""` | Secret that contains ca.crt that was used to sign cert for protecting Kuma in-cluster communication (ca.crt present in this secret have precedence over the one provided in the controlPlane.tls.general.secretName) | +| controlPlane.tls.general.caBundle | string | `""` | Base64 encoded CA certificate (the same as in controlPlane.tls.general.secret#ca.crt) | +| controlPlane.tls.apiServer.secretName | string | `""` | Secret that contains tls.crt, tls.key for protecting Kuma API on HTTPS | +| controlPlane.tls.apiServer.clientCertsSecretName | string | `""` | Secret that contains list of .pem certificates that can access admin endpoints of Kuma API on HTTPS | +| controlPlane.tls.kdsGlobalServer.secretName | string | `""` | Name of the K8s TLS Secret resource. If you set this and don't set create=true, you have to create the secret manually. | +| controlPlane.tls.kdsGlobalServer.create | bool | `false` | Whether to create the TLS secret in helm. | +| controlPlane.tls.kdsGlobalServer.cert | string | `""` | The TLS certificate to offer. | +| controlPlane.tls.kdsGlobalServer.key | string | `""` | The TLS key to use. | +| controlPlane.tls.kdsZoneClient.secretName | string | `""` | Name of the K8s Secret resource that contains ca.crt which was used to sign the certificate of KDS Global Server. If you set this and don't set create=true, you have to create the secret manually. | +| controlPlane.tls.kdsZoneClient.create | bool | `false` | Whether to create the TLS secret in helm. | +| controlPlane.tls.kdsZoneClient.cert | string | `""` | CA bundle that was used to sign the certificate of KDS Global Server. | +| controlPlane.tls.kdsZoneClient.skipVerify | bool | `false` | If true, TLS cert of the server is not verified. | +| controlPlane.serviceAccountAnnotations | object | `{}` | Annotations to add for Control Plane's Service Account | +| controlPlane.image.pullPolicy | string | `"IfNotPresent"` | Kuma CP ImagePullPolicy | +| controlPlane.image.repository | string | `"kuma-cp"` | Kuma CP image repository | +| controlPlane.image.tag | string | `nil` | Kuma CP Image tag. When not specified, the value is copied from global.tag | +| controlPlane.secrets | object with { Env: string, Secret: string, Key: string } | `nil` | Secrets to add as environment variables, where `Env` is the name of the env variable, `Secret` is the name of the Secret, and `Key` is the key of the Secret value to use | +| controlPlane.envVars | object | `{}` | Additional environment variables that will be passed to the control plane | +| controlPlane.extraConfigMaps | list | `[]` | Additional config maps to mount into the control plane, with optional inline values | +| controlPlane.extraSecrets | object with { name: string, mountPath: string, readOnly: string } | `nil` | Additional secrets to mount into the control plane, where `Env` is the name of the env variable, `Secret` is the name of the Secret, and `Key` is the key of the Secret value to use | +| controlPlane.webhooks.validator.additionalRules | string | `""` | Additional rules to apply on Kuma validator webhook. Useful when building custom policy on top of Kuma. | +| controlPlane.webhooks.ownerReference.additionalRules | string | `""` | Additional rules to apply on Kuma owner reference webhook. Useful when building custom policy on top of Kuma. | +| controlPlane.hostNetwork | bool | `false` | Specifies if the deployment should be started in hostNetwork mode. | +| controlPlane.admissionServerPort | int | `5443` | Define a new server port for the admission controller. Recommended to set in combination with hostNetwork to prevent multiple port bindings on the same port (like Calico in AWS EKS). | +| controlPlane.podSecurityContext | object | `{"runAsNonRoot":true}` | Security context at the pod level for control plane. | +| controlPlane.containerSecurityContext | object | `{"readOnlyRootFilesystem":true}` | Security context at the container level for control plane. | +| controlPlane.supportGatewaySecretsInAllNamespaces | bool | `false` | If true, then control plane can support TLS secrets for builtin gateway outside of mesh system namespace. The downside is that control plane requires permission to read Secrets in all namespaces. | +| cni.enabled | bool | `false` | Install Kuma with CNI instead of proxy init container | +| cni.chained | bool | `false` | Install CNI in chained mode | +| cni.netDir | string | `"/etc/cni/multus/net.d"` | Set the CNI install directory | +| cni.binDir | string | `"/var/lib/cni/bin"` | Set the CNI bin directory | +| cni.confName | string | `"kuma-cni.conf"` | Set the CNI configuration name | +| cni.logLevel | string | `"info"` | CNI log level: one of off,info,debug | +| cni.nodeSelector | object | `{"kubernetes.io/os":"linux"}` | Node Selector for the CNI pods | +| cni.tolerations | list | `[]` | Tolerations for the CNI pods | +| cni.podAnnotations | object | `{}` | Additional pod annotations | +| cni.namespace | string | `"kube-system"` | Set the CNI namespace | +| cni.image.repository | string | `"kuma-cni"` | CNI image repository | +| cni.image.tag | string | `nil` | CNI image tag - defaults to .Chart.AppVersion | +| cni.image.imagePullPolicy | string | `"IfNotPresent"` | CNI image pull policy | +| cni.delayStartupSeconds | int | `0` | it's only useful in tests to trigger a possible race condition | +| cni.experimental | object | `{"imageEbpf":{"registry":"docker.io/kumahq","repository":"merbridge","tag":"0.8.5"}}` | use new CNI (experimental) | +| cni.experimental.imageEbpf.registry | string | `"docker.io/kumahq"` | CNI experimental eBPF image registry | +| cni.experimental.imageEbpf.repository | string | `"merbridge"` | CNI experimental eBPF image repository | +| cni.experimental.imageEbpf.tag | string | `"0.8.5"` | CNI experimental eBPF image tag | +| cni.resources.requests.cpu | string | `"100m"` | | +| cni.resources.requests.memory | string | `"100Mi"` | | +| cni.resources.limits.memory | string | `"100Mi"` | | +| cni.podSecurityContext | object | `{}` | Security context at the pod level for cni | +| cni.containerSecurityContext | object | `{"readOnlyRootFilesystem":true,"runAsGroup":0,"runAsNonRoot":false,"runAsUser":0}` | Security context at the container level for cni | +| dataPlane.dnsLogging | bool | `false` | If true, then turn on CoreDNS query logging | +| dataPlane.image.repository | string | `"kuma-dp"` | The Kuma DP image repository | +| dataPlane.image.pullPolicy | string | `"IfNotPresent"` | Kuma DP ImagePullPolicy | +| dataPlane.image.tag | string | `nil` | Kuma DP Image Tag. When not specified, the value is copied from global.tag | +| dataPlane.initImage.repository | string | `"kuma-init"` | The Kuma DP init image repository | +| dataPlane.initImage.tag | string | `nil` | Kuma DP init image tag When not specified, the value is copied from global.tag | +| ingress.enabled | bool | `false` | If true, it deploys Ingress for cross cluster communication | +| ingress.extraLabels | object | `{}` | Labels to add to resources, in addition to default labels | +| ingress.drainTime | string | `"30s"` | Time for which old listener will still be active as draining | +| ingress.replicas | int | `1` | Number of replicas of the Ingress. Ignored when autoscaling is enabled. | +| ingress.logLevel | string | `"info"` | Log level for ingress (available values: off|info|debug) | +| ingress.resources | object | `{"limits":{"cpu":"1000m","memory":"512Mi"},"requests":{"cpu":"50m","memory":"64Mi"}}` | Define the resources to allocate to mesh ingress | +| ingress.lifecycle | object | `{}` | Pod lifecycle settings (useful for adding a preStop hook, when using AWS ALB or NLB) | +| ingress.terminationGracePeriodSeconds | int | `40` | Number of seconds to wait before force killing the pod. Make sure to update this if you add a preStop hook. | +| ingress.autoscaling.enabled | bool | `false` | Whether to enable Horizontal Pod Autoscaling, which requires the [Metrics Server](https://github.com/kubernetes-sigs/metrics-server) in the cluster | +| ingress.autoscaling.minReplicas | int | `2` | The minimum CP pods to allow | +| ingress.autoscaling.maxReplicas | int | `5` | The max CP pods to scale to | +| ingress.autoscaling.targetCPUUtilizationPercentage | int | `80` | For clusters that don't support autoscaling/v2, autoscaling/v1 is used | +| ingress.autoscaling.metrics | list | `[{"resource":{"name":"cpu","target":{"averageUtilization":80,"type":"Utilization"}},"type":"Resource"}]` | For clusters that do support autoscaling/v2, use metrics | +| ingress.service.enabled | bool | `true` | Whether to create a Service resource. | +| ingress.service.type | string | `"LoadBalancer"` | Service type of the Ingress | +| ingress.service.loadBalancerIP | string | `nil` | Optionally specify IP to be used by cloud provider when configuring load balancer | +| ingress.service.annotations | object | `{}` | Additional annotations to put on the Ingress service | +| ingress.service.port | int | `10001` | Port on which Ingress is exposed | +| ingress.service.nodePort | string | `nil` | Port on which service is exposed on Node for service of type NodePort | +| ingress.annotations | object | `{}` | Additional pod annotations (deprecated favor `podAnnotations`) | +| ingress.podAnnotations | object | `{}` | Additional pod annotations | +| ingress.nodeSelector | object | `{"kubernetes.io/os":"linux"}` | Node Selector for the Ingress pods | +| ingress.tolerations | list | `[]` | Tolerations for the Ingress pods | +| ingress.podDisruptionBudget.enabled | bool | `false` | Whether to create a pod disruption budget | +| ingress.podDisruptionBudget.maxUnavailable | int | `1` | The maximum number of unavailable pods allowed by the budget | +| ingress.affinity | object | `{"podAntiAffinity":{"preferredDuringSchedulingIgnoredDuringExecution":[{"podAffinityTerm":{"labelSelector":{"matchExpressions":[{"key":"app.kubernetes.io/name","operator":"In","values":["{{ include \"kuma.name\" . }}"]},{"key":"app.kubernetes.io/instance","operator":"In","values":["{{ .Release.Name }}"]},{"key":"app","operator":"In","values":["kuma-ingress"]}]},"topologyKey":"kubernetes.io/hostname"},"weight":100}]}}` | Affinity placement rule for the Kuma Ingress pods This is rendered as a template, so you can reference other helm variables or includes. | +| ingress.topologySpreadConstraints | string | `nil` | Topology spread constraints rule for the Kuma Mesh Ingress pods. This is rendered as a template, so you can use variables to generate match labels. | +| ingress.podSecurityContext | object | `{"runAsGroup":5678,"runAsNonRoot":true,"runAsUser":5678}` | Security context at the pod level for ingress | +| ingress.containerSecurityContext | object | `{"readOnlyRootFilesystem":true}` | Security context at the container level for ingress | +| ingress.serviceAccountAnnotations | object | `{}` | Annotations to add for Control Plane's Service Account | +| ingress.automountServiceAccountToken | bool | `true` | Whether to automountServiceAccountToken for cp. Optionally set to false | +| egress.enabled | bool | `false` | If true, it deploys Egress for cross cluster communication | +| egress.extraLabels | object | `{}` | Labels to add to resources, in addition to the default labels. | +| egress.drainTime | string | `"30s"` | Time for which old listener will still be active as draining | +| egress.replicas | int | `1` | Number of replicas of the Egress. Ignored when autoscaling is enabled. | +| egress.logLevel | string | `"info"` | Log level for egress (available values: off|info|debug) | +| egress.autoscaling.enabled | bool | `false` | Whether to enable Horizontal Pod Autoscaling, which requires the [Metrics Server](https://github.com/kubernetes-sigs/metrics-server) in the cluster | +| egress.autoscaling.minReplicas | int | `2` | The minimum CP pods to allow | +| egress.autoscaling.maxReplicas | int | `5` | The max CP pods to scale to | +| egress.autoscaling.targetCPUUtilizationPercentage | int | `80` | For clusters that don't support autoscaling/v2, autoscaling/v1 is used | +| egress.autoscaling.metrics | list | `[{"resource":{"name":"cpu","target":{"averageUtilization":80,"type":"Utilization"}},"type":"Resource"}]` | For clusters that do support autoscaling/v2, use metrics | +| egress.resources.requests.cpu | string | `"50m"` | | +| egress.resources.requests.memory | string | `"64Mi"` | | +| egress.resources.limits.cpu | string | `"1000m"` | | +| egress.resources.limits.memory | string | `"512Mi"` | | +| egress.service.enabled | bool | `true` | Whether to create the service object | +| egress.service.type | string | `"ClusterIP"` | Service type of the Egress | +| egress.service.loadBalancerIP | string | `nil` | Optionally specify IP to be used by cloud provider when configuring load balancer | +| egress.service.annotations | object | `{}` | Additional annotations to put on the Egress service | +| egress.service.port | int | `10002` | Port on which Egress is exposed | +| egress.service.nodePort | string | `nil` | Port on which service is exposed on Node for service of type NodePort | +| egress.annotations | object | `{}` | Additional pod annotations (deprecated favor `podAnnotations`) | +| egress.podAnnotations | object | `{}` | Additional pod annotations | +| egress.nodeSelector | object | `{"kubernetes.io/os":"linux"}` | Node Selector for the Egress pods | +| egress.tolerations | list | `[]` | Tolerations for the Egress pods | +| egress.podDisruptionBudget.enabled | bool | `false` | Whether to create a pod disruption budget | +| egress.podDisruptionBudget.maxUnavailable | int | `1` | The maximum number of unavailable pods allowed by the budget | +| egress.affinity | object | `{"podAntiAffinity":{"preferredDuringSchedulingIgnoredDuringExecution":[{"podAffinityTerm":{"labelSelector":{"matchExpressions":[{"key":"app.kubernetes.io/name","operator":"In","values":["{{ include \"kuma.name\" . }}"]},{"key":"app.kubernetes.io/instance","operator":"In","values":["{{ .Release.Name }}"]},{"key":"app","operator":"In","values":["kuma-egress"]}]},"topologyKey":"kubernetes.io/hostname"},"weight":100}]}}` | Affinity placement rule for the Kuma Egress pods. This is rendered as a template, so you can reference other helm variables or includes. | +| egress.topologySpreadConstraints | string | `nil` | Topology spread constraints rule for the Kuma Egress pods. This is rendered as a template, so you can use variables to generate match labels. | +| egress.podSecurityContext | object | `{"runAsGroup":5678,"runAsNonRoot":true,"runAsUser":5678}` | Security context at the pod level for egress | +| egress.containerSecurityContext | object | `{"readOnlyRootFilesystem":true}` | Security context at the container level for egress | +| egress.serviceAccountAnnotations | object | `{}` | Annotations to add for Control Plane's Service Account | +| egress.automountServiceAccountToken | bool | `true` | Whether to automountServiceAccountToken for cp. Optionally set to false | +| kumactl.image.repository | string | `"kumactl"` | The kumactl image repository | +| kumactl.image.tag | string | `nil` | The kumactl image tag. When not specified, the value is copied from global.tag | +| kubectl.image.registry | string | `"docker.io"` | The kubectl image registry | +| kubectl.image.repository | string | `"bitnami/kubectl"` | The kubectl image repository | +| kubectl.image.tag | string | `"1.27.5"` | The kubectl image tag | +| hooks.nodeSelector | object | `{"kubernetes.io/os":"linux"}` | Node selector for the HELM hooks | +| hooks.tolerations | list | `[]` | Tolerations for the HELM hooks | +| hooks.podSecurityContext | object | `{"runAsNonRoot":true}` | Security context at the pod level for crd/webhook/ns | +| hooks.containerSecurityContext | object | `{"readOnlyRootFilesystem":true}` | Security context at the container level for crd/webhook/ns | +| hooks.ebpfCleanup | object | `{"containerSecurityContext":{"readOnlyRootFilesystem":false},"podSecurityContext":{"runAsNonRoot":false}}` | ebpf-cleanup hook needs write access to the root filesystem to clean ebpf programs Changing below values will potentially break ebpf cleanup completely, so be cautious when doing so. | +| hooks.ebpfCleanup.podSecurityContext | object | `{"runAsNonRoot":false}` | Security context at the pod level for crd/webhook/cleanup-ebpf | +| hooks.ebpfCleanup.containerSecurityContext | object | `{"readOnlyRootFilesystem":false}` | Security context at the container level for crd/webhook/cleanup-ebpf | +| experimental.ebpf.enabled | bool | `false` | If true, ebpf will be used instead of using iptables to install/configure transparent proxy | +| experimental.ebpf.instanceIPEnvVarName | string | `"INSTANCE_IP"` | Name of the environmental variable which will contain the IP address of a pod | +| experimental.ebpf.bpffsPath | string | `"/sys/fs/bpf"` | Path where BPF file system should be mounted | +| experimental.ebpf.cgroupPath | string | `"/sys/fs/cgroup"` | Host's cgroup2 path | +| experimental.ebpf.tcAttachIface | string | `""` | Name of the network interface which TC programs should be attached to, we'll try to automatically determine it if empty | +| experimental.ebpf.programsSourcePath | string | `"/tmp/kuma-ebpf"` | Path where compiled eBPF programs which will be installed can be found | +| experimental.deltaKds | bool | `true` | If false, it uses legacy API for resource synchronization | +| experimental.sidecarContainers | bool | `false` | If true, enable native Kubernetes sidecars. This requires at least Kubernetes v1.29 | +| postgres.port | string | `"5432"` | Postgres port, password should be provided as a secret reference in "controlPlane.secrets" with the Env value "KUMA_STORE_POSTGRES_PASSWORD". Example: controlPlane: secrets: - Secret: postgres-postgresql Key: postgresql-password Env: KUMA_STORE_POSTGRES_PASSWORD | +| postgres.tls.mode | string | `"disable"` | Mode of TLS connection. Available values are: "disable", "verifyNone", "verifyCa", "verifyFull" | +| postgres.tls.disableSSLSNI | bool | `false` | Whether to disable SNI the postgres `sslsni` option. | +| postgres.tls.caSecretName | string | `nil` | Secret name that contains the ca.crt | +| postgres.tls.secretName | string | `nil` | Secret name that contains the client tls.crt, tls.key | + +## Custom Resource Definitions + +All Kuma CRDs are loaded via the [`crds`](crds) directory. For more detailed information on CRDs and Helm, +please refer to [the Helm documentation][helm-crd]. + +## Deleting + +As part of [Helm's limitations][helm-crd-limitations], CRDs will not be deleted when the `kuma` chart is deleted and +must be deleted manually. When a CRD is deleted Kubernetes deletes all resources of that kind as well, so this should +be done carefully. + +To do this with `kubectl` on *nix platforms, run: + +```shell +kubectl get crds | grep kuma.io | tr -s " " | cut -d " " -f1 | xargs kubectl delete crd + +# or with jq +kubectl get crds -o json | jq '.items[].metadata.name | select(.|test(".*kuma\\.io"))' | xargs kubectl delete crd +``` + +## Autoscaling + +In production, it is advisable to enable Control Plane autoscaling for High Availability. Autoscaling uses the +`HorizontalPodAutoscaler` resource to add redundancy and scale the CP pods based on CPU utilization, which requires +the [k8s metrics-server][kube-metrics-server] to be running on the cluster. + +## Development + +The charts are used internally in `kumactl install`, therefore the following rules apply when developing new chat features: + * all templates that start with `pre-` and `post-` are omitted when processing in `kumactl install` + +### Installing Metrics Server for Autoscaling + +If running on kind, or on a cluster with a similarly self-signed cert, the metrics server must be configured to allow +insecure kubelet TLS. The make task `kind/deploy/metrics-server` installs this patched version of the server. + +[kuma-url]: https://kuma.io/ +[kuma-logo]: https://kuma-public-assets.s3.amazonaws.com/kuma-logo-v2.png +[helm-crd]: https://helm.sh/docs/chart_best_practices/custom_resource_definitions/ +[helm-crd-limitations]: https://helm.sh/docs/topics/charts/#limitations-on-crds +[kube-metrics-server]: https://github.com/kubernetes-sigs/metrics-server diff --git a/charts/kuma/kuma/2.8.3/README.md.gotmpl b/charts/kuma/kuma/2.8.3/README.md.gotmpl new file mode 100644 index 0000000000..3b296a411c --- /dev/null +++ b/charts/kuma/kuma/2.8.3/README.md.gotmpl @@ -0,0 +1,52 @@ +[![][kuma-logo]][kuma-url] + +{{ template "chart.description" . }} + +{{ template "chart.typeBadge" . }}{{ template "chart.versionBadge" . }}{{ template "chart.appVersionBadge" . }} + +{{ template "chart.homepageLine" . }} + +{{ template "chart.valuesSection" . }} + +## Custom Resource Definitions + +All Kuma CRDs are loaded via the [`crds`](crds) directory. For more detailed information on CRDs and Helm, +please refer to [the Helm documentation][helm-crd]. + +## Deleting + +As part of [Helm's limitations][helm-crd-limitations], CRDs will not be deleted when the `kuma` chart is deleted and +must be deleted manually. When a CRD is deleted Kubernetes deletes all resources of that kind as well, so this should +be done carefully. + +To do this with `kubectl` on *nix platforms, run: + +```shell +kubectl get crds | grep kuma.io | tr -s " " | cut -d " " -f1 | xargs kubectl delete crd + +# or with jq +kubectl get crds -o json | jq '.items[].metadata.name | select(.|test(".*kuma\\.io"))' | xargs kubectl delete crd +``` + +## Autoscaling + +In production, it is advisable to enable Control Plane autoscaling for High Availability. Autoscaling uses the +`HorizontalPodAutoscaler` resource to add redundancy and scale the CP pods based on CPU utilization, which requires +the [k8s metrics-server][kube-metrics-server] to be running on the cluster. + +## Development + +The charts are used internally in `kumactl install`, therefore the following rules apply when developing new chat features: + * all templates that start with `pre-` and `post-` are omitted when processing in `kumactl install` + +### Installing Metrics Server for Autoscaling + +If running on kind, or on a cluster with a similarly self-signed cert, the metrics server must be configured to allow +insecure kubelet TLS. The make task `kind/deploy/metrics-server` installs this patched version of the server. + + +[kuma-url]: https://kuma.io/ +[kuma-logo]: https://kuma-public-assets.s3.amazonaws.com/kuma-logo-v2.png +[helm-crd]: https://helm.sh/docs/chart_best_practices/custom_resource_definitions/ +[helm-crd-limitations]: https://helm.sh/docs/topics/charts/#limitations-on-crds +[kube-metrics-server]: https://github.com/kubernetes-sigs/metrics-server diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_circuitbreakers.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_circuitbreakers.yaml new file mode 100644 index 0000000000..449e4eb811 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_circuitbreakers.yaml @@ -0,0 +1,50 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: circuitbreakers.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: CircuitBreaker + listKind: CircuitBreakerList + plural: circuitbreakers + singular: circuitbreaker + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + 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 + mesh: + description: |- + Mesh is the name of the Kuma mesh this resource belongs to. + It may be omitted for cluster-scoped resources. + type: string + metadata: + type: object + spec: + description: Spec is the specification of the Kuma CircuitBreaker resource. + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: true diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_containerpatches.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_containerpatches.yaml new file mode 100644 index 0000000000..654bbf9288 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_containerpatches.yaml @@ -0,0 +1,114 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: containerpatches.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: ContainerPatch + listKind: ContainerPatchList + plural: containerpatches + singular: containerpatch + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: ContainerPatch stores a list of patches to apply to init and + sidecar containers. + 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 + mesh: + type: string + metadata: + type: object + spec: + description: ContainerPatchSpec specifies the options available for a + ContainerPatch + properties: + initPatch: + description: InitPatch specifies jsonpatch to apply to an init container. + items: + description: JsonPatchBlock is one json patch operation block. + properties: + from: + description: From is a jsonpatch from string, used by move and + copy operations. + type: string + op: + description: Op is a jsonpatch operation string. + enum: + - add + - remove + - replace + - move + - copy + type: string + path: + description: Path is a jsonpatch path string. + type: string + value: + description: |- + Value must be a string representing a valid json object used + by replace and add operations. String has to be escaped with " to be valid a json object. + type: string + required: + - op + - path + type: object + type: array + sidecarPatch: + description: SidecarPatch specifies jsonpatch to apply to a sidecar + container. + items: + description: JsonPatchBlock is one json patch operation block. + properties: + from: + description: From is a jsonpatch from string, used by move and + copy operations. + type: string + op: + description: Op is a jsonpatch operation string. + enum: + - add + - remove + - replace + - move + - copy + type: string + path: + description: Path is a jsonpatch path string. + type: string + value: + description: |- + Value must be a string representing a valid json object used + by replace and add operations. String has to be escaped with " to be valid a json object. + type: string + required: + - op + - path + type: object + type: array + type: object + type: object + served: true + storage: true diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_dataplaneinsights.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_dataplaneinsights.yaml new file mode 100644 index 0000000000..b184e19556 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_dataplaneinsights.yaml @@ -0,0 +1,50 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: dataplaneinsights.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: DataplaneInsight + listKind: DataplaneInsightList + plural: dataplaneinsights + singular: dataplaneinsight + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + 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 + mesh: + description: |- + Mesh is the name of the Kuma mesh this resource belongs to. + It may be omitted for cluster-scoped resources. + type: string + metadata: + type: object + status: + description: Status is the status the Kuma resource. + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: true diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_dataplanes.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_dataplanes.yaml new file mode 100644 index 0000000000..9d0be07cde --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_dataplanes.yaml @@ -0,0 +1,70 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: dataplanes.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: Dataplane + listKind: DataplaneList + plural: dataplanes + singular: dataplane + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Service tag of the first inbound + jsonPath: .spec.networking.inbound[0].tags['kuma\.io/service'] + name: kuma.io/service + type: string + - description: Service tag of the second inbound + jsonPath: .spec.networking.inbound[1].tags['kuma\.io/service'] + name: kuma.io/service + type: string + - description: Service tag of the third inbound + jsonPath: .spec.networking.inbound[2].tags['kuma\.io/service'] + name: kuma.io/service + priority: 1 + type: string + - description: Service tag of the fourth inbound + jsonPath: .spec.networking.inbound[3].tags['kuma\.io/service'] + name: kuma.io/service + priority: 1 + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + 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 + mesh: + description: |- + Mesh is the name of the Kuma mesh this resource belongs to. + It may be omitted for cluster-scoped resources. + type: string + metadata: + type: object + spec: + description: Spec is the specification of the Kuma Dataplane resource. + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: true + subresources: {} diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_externalservices.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_externalservices.yaml new file mode 100644 index 0000000000..038ea3f7aa --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_externalservices.yaml @@ -0,0 +1,50 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: externalservices.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: ExternalService + listKind: ExternalServiceList + plural: externalservices + singular: externalservice + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + 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 + mesh: + description: |- + Mesh is the name of the Kuma mesh this resource belongs to. + It may be omitted for cluster-scoped resources. + type: string + metadata: + type: object + spec: + description: Spec is the specification of the Kuma ExternalService resource. + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: true diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_faultinjections.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_faultinjections.yaml new file mode 100644 index 0000000000..93ce367fcb --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_faultinjections.yaml @@ -0,0 +1,50 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: faultinjections.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: FaultInjection + listKind: FaultInjectionList + plural: faultinjections + singular: faultinjection + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + 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 + mesh: + description: |- + Mesh is the name of the Kuma mesh this resource belongs to. + It may be omitted for cluster-scoped resources. + type: string + metadata: + type: object + spec: + description: Spec is the specification of the Kuma FaultInjection resource. + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: true diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_healthchecks.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_healthchecks.yaml new file mode 100644 index 0000000000..9599e09dd8 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_healthchecks.yaml @@ -0,0 +1,50 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: healthchecks.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: HealthCheck + listKind: HealthCheckList + plural: healthchecks + singular: healthcheck + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + 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 + mesh: + description: |- + Mesh is the name of the Kuma mesh this resource belongs to. + It may be omitted for cluster-scoped resources. + type: string + metadata: + type: object + spec: + description: Spec is the specification of the Kuma HealthCheck resource. + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: true diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_hostnamegenerators.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_hostnamegenerators.yaml new file mode 100644 index 0000000000..289ba10ce6 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_hostnamegenerators.yaml @@ -0,0 +1,65 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: hostnamegenerators.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: HostnameGenerator + listKind: HostnameGeneratorList + plural: hostnamegenerators + singular: hostnamegenerator + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + 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 is the specification of the Kuma HostnameGenerator resource. + properties: + selector: + properties: + meshExternalService: + properties: + matchLabels: + additionalProperties: + type: string + type: object + type: object + meshService: + properties: + matchLabels: + additionalProperties: + type: string + type: object + type: object + type: object + template: + type: string + type: object + type: object + served: true + storage: true diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_meshaccesslogs.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshaccesslogs.yaml new file mode 100644 index 0000000000..61cb8c28a5 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshaccesslogs.yaml @@ -0,0 +1,556 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: meshaccesslogs.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: MeshAccessLog + listKind: MeshAccessLogList + plural: meshaccesslogs + singular: meshaccesslog + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.targetRef.kind + name: TargetRef Kind + type: string + - jsonPath: .spec.targetRef.name + name: TargetRef Name + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + 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 is the specification of the Kuma MeshAccessLog resource. + properties: + from: + description: From list makes a match between clients and corresponding + configurations + items: + properties: + default: + description: |- + Default is a configuration specific to the group of clients referenced in + 'targetRef' + properties: + backends: + items: + properties: + file: + description: FileBackend defines configuration for + file based access logs + properties: + format: + description: |- + Format of access logs. Placeholders available on + https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators + properties: + json: + example: + - key: start_time + value: '%START_TIME%' + - key: bytes_received + value: '%BYTES_RECEIVED%' + items: + properties: + key: + type: string + value: + type: string + type: object + type: array + omitEmptyValues: + default: false + type: boolean + plain: + example: '[%START_TIME%] %KUMA_MESH% %UPSTREAM_HOST%' + type: string + type: + enum: + - Plain + - Json + type: string + required: + - type + type: object + path: + description: Path to a file that logs will be + written to + example: /tmp/access.log + minLength: 1 + type: string + required: + - path + type: object + openTelemetry: + description: Defines an OpenTelemetry logging backend. + properties: + attributes: + description: |- + Attributes can contain placeholders available on + https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators + example: + - key: mesh + value: '%KUMA_MESH%' + items: + properties: + key: + type: string + value: + type: string + type: object + type: array + body: + description: |- + Body is a raw string or an OTLP any value as described at + https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/data-model.md#field-body + It can contain placeholders available on + https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators + example: + kvlistValue: + values: + - key: mesh + value: + stringValue: '%KUMA_MESH%' + x-kubernetes-preserve-unknown-fields: true + endpoint: + description: Endpoint of OpenTelemetry collector. + An empty port defaults to 4317. + example: otel-collector:4317 + minLength: 1 + type: string + required: + - endpoint + type: object + tcp: + description: TCPBackend defines a TCP logging backend. + properties: + address: + description: Address of the TCP logging backend + example: 127.0.0.1:5000 + minLength: 1 + type: string + format: + description: |- + Format of access logs. Placeholders available on + https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators + properties: + json: + example: + - key: start_time + value: '%START_TIME%' + - key: bytes_received + value: '%BYTES_RECEIVED%' + items: + properties: + key: + type: string + value: + type: string + type: object + type: array + omitEmptyValues: + default: false + type: boolean + plain: + example: '[%START_TIME%] %KUMA_MESH% %UPSTREAM_HOST%' + type: string + type: + enum: + - Plain + - Json + type: string + required: + - type + type: object + required: + - address + type: object + type: + enum: + - Tcp + - File + - OpenTelemetry + type: string + required: + - type + type: object + type: array + type: object + targetRef: + description: |- + TargetRef is a reference to the resource that represents a group of + clients. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify + cross mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + required: + - targetRef + type: object + type: array + targetRef: + description: |- + TargetRef is a reference to the resource the policy takes an effect on. + The resource could be either a real store object or virtual resource + defined in-place. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify cross + mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + to: + description: To list makes a match between the consumed services and + corresponding configurations + items: + properties: + default: + description: |- + Default is a configuration specific to the group of destinations referenced in + 'targetRef' + properties: + backends: + items: + properties: + file: + description: FileBackend defines configuration for + file based access logs + properties: + format: + description: |- + Format of access logs. Placeholders available on + https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators + properties: + json: + example: + - key: start_time + value: '%START_TIME%' + - key: bytes_received + value: '%BYTES_RECEIVED%' + items: + properties: + key: + type: string + value: + type: string + type: object + type: array + omitEmptyValues: + default: false + type: boolean + plain: + example: '[%START_TIME%] %KUMA_MESH% %UPSTREAM_HOST%' + type: string + type: + enum: + - Plain + - Json + type: string + required: + - type + type: object + path: + description: Path to a file that logs will be + written to + example: /tmp/access.log + minLength: 1 + type: string + required: + - path + type: object + openTelemetry: + description: Defines an OpenTelemetry logging backend. + properties: + attributes: + description: |- + Attributes can contain placeholders available on + https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators + example: + - key: mesh + value: '%KUMA_MESH%' + items: + properties: + key: + type: string + value: + type: string + type: object + type: array + body: + description: |- + Body is a raw string or an OTLP any value as described at + https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/data-model.md#field-body + It can contain placeholders available on + https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators + example: + kvlistValue: + values: + - key: mesh + value: + stringValue: '%KUMA_MESH%' + x-kubernetes-preserve-unknown-fields: true + endpoint: + description: Endpoint of OpenTelemetry collector. + An empty port defaults to 4317. + example: otel-collector:4317 + minLength: 1 + type: string + required: + - endpoint + type: object + tcp: + description: TCPBackend defines a TCP logging backend. + properties: + address: + description: Address of the TCP logging backend + example: 127.0.0.1:5000 + minLength: 1 + type: string + format: + description: |- + Format of access logs. Placeholders available on + https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators + properties: + json: + example: + - key: start_time + value: '%START_TIME%' + - key: bytes_received + value: '%BYTES_RECEIVED%' + items: + properties: + key: + type: string + value: + type: string + type: object + type: array + omitEmptyValues: + default: false + type: boolean + plain: + example: '[%START_TIME%] %KUMA_MESH% %UPSTREAM_HOST%' + type: string + type: + enum: + - Plain + - Json + type: string + required: + - type + type: object + required: + - address + type: object + type: + enum: + - Tcp + - File + - OpenTelemetry + type: string + required: + - type + type: object + type: array + type: object + targetRef: + description: |- + TargetRef is a reference to the resource that represents a group of + destinations. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify + cross mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + required: + - targetRef + type: object + type: array + required: + - targetRef + type: object + type: object + served: true + storage: true + subresources: {} diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_meshcircuitbreakers.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshcircuitbreakers.yaml new file mode 100644 index 0000000000..b0b848b52f --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshcircuitbreakers.yaml @@ -0,0 +1,738 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: meshcircuitbreakers.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: MeshCircuitBreaker + listKind: MeshCircuitBreakerList + plural: meshcircuitbreakers + singular: meshcircuitbreaker + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.targetRef.kind + name: TargetRef Kind + type: string + - jsonPath: .spec.targetRef.name + name: TargetRef Name + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + 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 is the specification of the Kuma MeshCircuitBreaker + resource. + properties: + from: + description: From list makes a match between clients and corresponding + configurations + items: + properties: + default: + description: |- + Default is a configuration specific to the group of destinations + referenced in 'targetRef' + properties: + connectionLimits: + description: |- + ConnectionLimits contains configuration of each circuit breaking limit, + which when exceeded makes the circuit breaker to become open (no traffic + is allowed like no current is allowed in the circuits when physical + circuit breaker ir open) + properties: + maxConnectionPools: + description: |- + The maximum number of connection pools per cluster that are concurrently + supported at once. Set this for clusters which create a large number of + connection pools. + format: int32 + type: integer + maxConnections: + description: |- + The maximum number of connections allowed to be made to the upstream + cluster. + format: int32 + type: integer + maxPendingRequests: + description: |- + The maximum number of pending requests that are allowed to the upstream + cluster. This limit is applied as a connection limit for non-HTTP + traffic. + format: int32 + type: integer + maxRequests: + description: |- + The maximum number of parallel requests that are allowed to be made + to the upstream cluster. This limit does not apply to non-HTTP traffic. + format: int32 + type: integer + maxRetries: + description: |- + The maximum number of parallel retries that will be allowed to + the upstream cluster. + format: int32 + type: integer + type: object + outlierDetection: + description: |- + OutlierDetection contains the configuration of the process of dynamically + determining whether some number of hosts in an upstream cluster are + performing unlike the others and removing them from the healthy load + balancing set. Performance might be along different axes such as + consecutive failures, temporal success rate, temporal latency, etc. + Outlier detection is a form of passive health checking. + properties: + baseEjectionTime: + description: |- + The base time that a host is ejected for. The real time is equal to + the base time multiplied by the number of times the host has been + ejected. + type: string + detectors: + description: Contains configuration for supported outlier + detectors + properties: + failurePercentage: + description: |- + Failure Percentage based outlier detection functions similarly to success + rate detection, in that it relies on success rate data from each host in + a cluster. However, rather than compare those values to the mean success + rate of the cluster as a whole, they are compared to a flat + user-configured threshold. This threshold is configured via the + outlierDetection.failurePercentageThreshold field. + The other configuration fields for failure percentage based detection are + similar to the fields for success rate detection. As with success rate + detection, detection will not be performed for a host if its request + volume over the aggregation interval is less than the + outlierDetection.detectors.failurePercentage.requestVolume value. + Detection also will not be performed for a cluster if the number of hosts + with the minimum required request volume in an interval is less than the + outlierDetection.detectors.failurePercentage.minimumHosts value. + properties: + minimumHosts: + description: |- + The minimum number of hosts in a cluster in order to perform failure + percentage-based ejection. If the total number of hosts in the cluster is + less than this value, failure percentage-based ejection will not be + performed. + format: int32 + type: integer + requestVolume: + description: |- + The minimum number of total requests that must be collected in one + interval (as defined by the interval duration above) to perform failure + percentage-based ejection for this host. If the volume is lower than this + setting, failure percentage-based ejection will not be performed for this + host. + format: int32 + type: integer + threshold: + description: |- + The failure percentage to use when determining failure percentage-based + outlier detection. If the failure percentage of a given host is greater + than or equal to this value, it will be ejected. + format: int32 + type: integer + type: object + gatewayFailures: + description: |- + In the default mode (outlierDetection.splitExternalLocalOriginErrors is + false) this detection type takes into account a subset of 5xx errors, + called "gateway errors" (502, 503 or 504 status code) and local origin + failures, such as timeout, TCP reset etc. + In split mode (outlierDetection.splitExternalLocalOriginErrors is true) + this detection type takes into account a subset of 5xx errors, called + "gateway errors" (502, 503 or 504 status code) and is supported only by + the http router. + properties: + consecutive: + description: |- + The number of consecutive gateway failures (502, 503, 504 status codes) + before a consecutive gateway failure ejection occurs. + format: int32 + type: integer + type: object + localOriginFailures: + description: |- + This detection type is enabled only when + outlierDetection.splitExternalLocalOriginErrors is true and takes into + account only locally originated errors (timeout, reset, etc). + If Envoy repeatedly cannot connect to an upstream host or communication + with the upstream host is repeatedly interrupted, it will be ejected. + Various locally originated problems are detected: timeout, TCP reset, + ICMP errors, etc. This detection type is supported by http router and + tcp proxy. + properties: + consecutive: + description: |- + The number of consecutive locally originated failures before ejection + occurs. Parameter takes effect only when splitExternalAndLocalErrors + is set to true. + format: int32 + type: integer + type: object + successRate: + description: |- + Success Rate based outlier detection aggregates success rate data from + every host in a cluster. Then at given intervals ejects hosts based on + statistical outlier detection. Success Rate outlier detection will not be + calculated for a host if its request volume over the aggregation interval + is less than the outlierDetection.detectors.successRate.requestVolume + value. + Moreover, detection will not be performed for a cluster if the number of + hosts with the minimum required request volume in an interval is less + than the outlierDetection.detectors.successRate.minimumHosts value. + In the default configuration mode + (outlierDetection.splitExternalLocalOriginErrors is false) this detection + type takes into account all types of errors: locally and externally + originated. + In split mode (outlierDetection.splitExternalLocalOriginErrors is true), + locally originated errors and externally originated (transaction) errors + are counted and treated separately. + properties: + minimumHosts: + description: |- + The number of hosts in a cluster that must have enough request volume to + detect success rate outliers. If the number of hosts is less than this + setting, outlier detection via success rate statistics is not performed + for any host in the cluster. + format: int32 + type: integer + requestVolume: + description: |- + The minimum number of total requests that must be collected in one + interval (as defined by the interval duration configured in + outlierDetection section) to include this host in success rate based + outlier detection. If the volume is lower than this setting, outlier + detection via success rate statistics is not performed for that host. + format: int32 + type: integer + standardDeviationFactor: + anyOf: + - type: integer + - type: string + description: |- + This factor is used to determine the ejection threshold for success rate + outlier ejection. The ejection threshold is the difference between + the mean success rate, and the product of this factor and the standard + deviation of the mean success rate: mean - (standard_deviation * + success_rate_standard_deviation_factor). + Either int or decimal represented as string. + x-kubernetes-int-or-string: true + type: object + totalFailures: + description: |- + In the default mode (outlierDetection.splitExternalAndLocalErrors is + false) this detection type takes into account all generated errors: + locally originated and externally originated (transaction) errors. + In split mode (outlierDetection.splitExternalLocalOriginErrors is true) + this detection type takes into account only externally originated + (transaction) errors, ignoring locally originated errors. + If an upstream host is an HTTP-server, only 5xx types of error are taken + into account (see Consecutive Gateway Failure for exceptions). + Properly formatted responses, even when they carry an operational error + (like index not found, access denied) are not taken into account. + properties: + consecutive: + description: |- + The number of consecutive server-side error responses (for HTTP traffic, + 5xx responses; for TCP traffic, connection failures; for Redis, failure + to respond PONG; etc.) before a consecutive total failure ejection + occurs. + format: int32 + type: integer + type: object + type: object + disabled: + description: When set to true, outlierDetection configuration + won't take any effect + type: boolean + interval: + description: |- + The time interval between ejection analysis sweeps. This can result in + both new ejections and hosts being returned to service. + type: string + maxEjectionPercent: + description: |- + The maximum % of an upstream cluster that can be ejected due to outlier + detection. Defaults to 10% but will eject at least one host regardless of + the value. + format: int32 + type: integer + splitExternalAndLocalErrors: + description: |- + Determines whether to distinguish local origin failures from external + errors. If set to true the following configuration parameters are taken + into account: detectors.localOriginFailures.consecutive + type: boolean + type: object + type: object + targetRef: + description: |- + TargetRef is a reference to the resource that represents a group of + destinations. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify + cross mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + required: + - targetRef + type: object + type: array + targetRef: + description: |- + TargetRef is a reference to the resource the policy takes an effect on. + The resource could be either a real store object or virtual resource + defined in place. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify cross + mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + to: + description: |- + To list makes a match between the consumed services and corresponding + configurations + items: + properties: + default: + description: |- + Default is a configuration specific to the group of destinations + referenced in 'targetRef' + properties: + connectionLimits: + description: |- + ConnectionLimits contains configuration of each circuit breaking limit, + which when exceeded makes the circuit breaker to become open (no traffic + is allowed like no current is allowed in the circuits when physical + circuit breaker ir open) + properties: + maxConnectionPools: + description: |- + The maximum number of connection pools per cluster that are concurrently + supported at once. Set this for clusters which create a large number of + connection pools. + format: int32 + type: integer + maxConnections: + description: |- + The maximum number of connections allowed to be made to the upstream + cluster. + format: int32 + type: integer + maxPendingRequests: + description: |- + The maximum number of pending requests that are allowed to the upstream + cluster. This limit is applied as a connection limit for non-HTTP + traffic. + format: int32 + type: integer + maxRequests: + description: |- + The maximum number of parallel requests that are allowed to be made + to the upstream cluster. This limit does not apply to non-HTTP traffic. + format: int32 + type: integer + maxRetries: + description: |- + The maximum number of parallel retries that will be allowed to + the upstream cluster. + format: int32 + type: integer + type: object + outlierDetection: + description: |- + OutlierDetection contains the configuration of the process of dynamically + determining whether some number of hosts in an upstream cluster are + performing unlike the others and removing them from the healthy load + balancing set. Performance might be along different axes such as + consecutive failures, temporal success rate, temporal latency, etc. + Outlier detection is a form of passive health checking. + properties: + baseEjectionTime: + description: |- + The base time that a host is ejected for. The real time is equal to + the base time multiplied by the number of times the host has been + ejected. + type: string + detectors: + description: Contains configuration for supported outlier + detectors + properties: + failurePercentage: + description: |- + Failure Percentage based outlier detection functions similarly to success + rate detection, in that it relies on success rate data from each host in + a cluster. However, rather than compare those values to the mean success + rate of the cluster as a whole, they are compared to a flat + user-configured threshold. This threshold is configured via the + outlierDetection.failurePercentageThreshold field. + The other configuration fields for failure percentage based detection are + similar to the fields for success rate detection. As with success rate + detection, detection will not be performed for a host if its request + volume over the aggregation interval is less than the + outlierDetection.detectors.failurePercentage.requestVolume value. + Detection also will not be performed for a cluster if the number of hosts + with the minimum required request volume in an interval is less than the + outlierDetection.detectors.failurePercentage.minimumHosts value. + properties: + minimumHosts: + description: |- + The minimum number of hosts in a cluster in order to perform failure + percentage-based ejection. If the total number of hosts in the cluster is + less than this value, failure percentage-based ejection will not be + performed. + format: int32 + type: integer + requestVolume: + description: |- + The minimum number of total requests that must be collected in one + interval (as defined by the interval duration above) to perform failure + percentage-based ejection for this host. If the volume is lower than this + setting, failure percentage-based ejection will not be performed for this + host. + format: int32 + type: integer + threshold: + description: |- + The failure percentage to use when determining failure percentage-based + outlier detection. If the failure percentage of a given host is greater + than or equal to this value, it will be ejected. + format: int32 + type: integer + type: object + gatewayFailures: + description: |- + In the default mode (outlierDetection.splitExternalLocalOriginErrors is + false) this detection type takes into account a subset of 5xx errors, + called "gateway errors" (502, 503 or 504 status code) and local origin + failures, such as timeout, TCP reset etc. + In split mode (outlierDetection.splitExternalLocalOriginErrors is true) + this detection type takes into account a subset of 5xx errors, called + "gateway errors" (502, 503 or 504 status code) and is supported only by + the http router. + properties: + consecutive: + description: |- + The number of consecutive gateway failures (502, 503, 504 status codes) + before a consecutive gateway failure ejection occurs. + format: int32 + type: integer + type: object + localOriginFailures: + description: |- + This detection type is enabled only when + outlierDetection.splitExternalLocalOriginErrors is true and takes into + account only locally originated errors (timeout, reset, etc). + If Envoy repeatedly cannot connect to an upstream host or communication + with the upstream host is repeatedly interrupted, it will be ejected. + Various locally originated problems are detected: timeout, TCP reset, + ICMP errors, etc. This detection type is supported by http router and + tcp proxy. + properties: + consecutive: + description: |- + The number of consecutive locally originated failures before ejection + occurs. Parameter takes effect only when splitExternalAndLocalErrors + is set to true. + format: int32 + type: integer + type: object + successRate: + description: |- + Success Rate based outlier detection aggregates success rate data from + every host in a cluster. Then at given intervals ejects hosts based on + statistical outlier detection. Success Rate outlier detection will not be + calculated for a host if its request volume over the aggregation interval + is less than the outlierDetection.detectors.successRate.requestVolume + value. + Moreover, detection will not be performed for a cluster if the number of + hosts with the minimum required request volume in an interval is less + than the outlierDetection.detectors.successRate.minimumHosts value. + In the default configuration mode + (outlierDetection.splitExternalLocalOriginErrors is false) this detection + type takes into account all types of errors: locally and externally + originated. + In split mode (outlierDetection.splitExternalLocalOriginErrors is true), + locally originated errors and externally originated (transaction) errors + are counted and treated separately. + properties: + minimumHosts: + description: |- + The number of hosts in a cluster that must have enough request volume to + detect success rate outliers. If the number of hosts is less than this + setting, outlier detection via success rate statistics is not performed + for any host in the cluster. + format: int32 + type: integer + requestVolume: + description: |- + The minimum number of total requests that must be collected in one + interval (as defined by the interval duration configured in + outlierDetection section) to include this host in success rate based + outlier detection. If the volume is lower than this setting, outlier + detection via success rate statistics is not performed for that host. + format: int32 + type: integer + standardDeviationFactor: + anyOf: + - type: integer + - type: string + description: |- + This factor is used to determine the ejection threshold for success rate + outlier ejection. The ejection threshold is the difference between + the mean success rate, and the product of this factor and the standard + deviation of the mean success rate: mean - (standard_deviation * + success_rate_standard_deviation_factor). + Either int or decimal represented as string. + x-kubernetes-int-or-string: true + type: object + totalFailures: + description: |- + In the default mode (outlierDetection.splitExternalAndLocalErrors is + false) this detection type takes into account all generated errors: + locally originated and externally originated (transaction) errors. + In split mode (outlierDetection.splitExternalLocalOriginErrors is true) + this detection type takes into account only externally originated + (transaction) errors, ignoring locally originated errors. + If an upstream host is an HTTP-server, only 5xx types of error are taken + into account (see Consecutive Gateway Failure for exceptions). + Properly formatted responses, even when they carry an operational error + (like index not found, access denied) are not taken into account. + properties: + consecutive: + description: |- + The number of consecutive server-side error responses (for HTTP traffic, + 5xx responses; for TCP traffic, connection failures; for Redis, failure + to respond PONG; etc.) before a consecutive total failure ejection + occurs. + format: int32 + type: integer + type: object + type: object + disabled: + description: When set to true, outlierDetection configuration + won't take any effect + type: boolean + interval: + description: |- + The time interval between ejection analysis sweeps. This can result in + both new ejections and hosts being returned to service. + type: string + maxEjectionPercent: + description: |- + The maximum % of an upstream cluster that can be ejected due to outlier + detection. Defaults to 10% but will eject at least one host regardless of + the value. + format: int32 + type: integer + splitExternalAndLocalErrors: + description: |- + Determines whether to distinguish local origin failures from external + errors. If set to true the following configuration parameters are taken + into account: detectors.localOriginFailures.consecutive + type: boolean + type: object + type: object + targetRef: + description: |- + TargetRef is a reference to the resource that represents a group of + destinations. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify + cross mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + required: + - targetRef + type: object + type: array + required: + - targetRef + type: object + type: object + served: true + storage: true + subresources: {} diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_meshes.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshes.yaml new file mode 100644 index 0000000000..5b7a9fd650 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshes.yaml @@ -0,0 +1,50 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: meshes.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: Mesh + listKind: MeshList + plural: meshes + singular: mesh + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + 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 + mesh: + description: |- + Mesh is the name of the Kuma mesh this resource belongs to. + It may be omitted for cluster-scoped resources. + type: string + metadata: + type: object + spec: + description: Spec is the specification of the Kuma Mesh resource. + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: true diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_meshexternalservices.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshexternalservices.yaml new file mode 100644 index 0000000000..6108163ca2 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshexternalservices.yaml @@ -0,0 +1,333 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: meshexternalservices.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: MeshExternalService + listKind: MeshExternalServiceList + plural: meshexternalservices + singular: meshexternalservice + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + 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 is the specification of the Kuma MeshExternalService + resource. + properties: + endpoints: + description: Endpoints defines a list of destinations to send traffic + to. + items: + properties: + address: + description: Address defines an address to which a user want + to send a request. Is possible to provide `domain`, `ip` and + `unix` sockets. + example: unix:///tmp/example.sock + minLength: 1 + type: string + port: + description: Port of the endpoint + maximum: 65535 + minimum: 1 + type: integer + required: + - address + type: object + type: array + extension: + description: Extension struct for a plugin configuration, in the presence + of an extension `endpoints` and `tls` are not required anymore - + it's up to the extension to validate them independently. + properties: + config: + description: Config freeform configuration for the extension. + x-kubernetes-preserve-unknown-fields: true + type: + description: Type of the extension. + type: string + required: + - config + - type + type: object + match: + description: Match defines traffic that should be routed through the + sidecar. + properties: + port: + description: Port defines a port to which a user does request. + maximum: 65535 + minimum: 1 + type: integer + protocol: + default: tcp + description: 'Protocol defines a protocol of the communication. + Possible values: `tcp`, `grpc`, `http`, `http2`.' + enum: + - tcp + - grpc + - http + - http2 + type: string + type: + default: HostnameGenerator + description: Type of the match, only `HostnameGenerator` is available + at the moment. + enum: + - HostnameGenerator + type: string + required: + - port + type: object + tls: + description: Tls provides a TLS configuration when proxy is resposible + for a TLS origination + properties: + allowRenegotiation: + default: false + description: |- + AllowRenegotiation defines if TLS sessions will allow renegotiation. + Setting this to true is not recommended for security reasons. + type: boolean + enabled: + default: false + description: Enabled defines if proxy should originate TLS. + type: boolean + verification: + description: Verification section for providing TLS verification + details. + properties: + caCert: + description: CaCert defines a certificate of CA. + properties: + inline: + description: Data source is inline bytes. + format: byte + type: string + inlineString: + description: Data source is inline string` + type: string + secret: + description: Data source is a secret with given Secret + key. + type: string + type: object + clientCert: + description: ClientCert defines a certificate of a client. + properties: + inline: + description: Data source is inline bytes. + format: byte + type: string + inlineString: + description: Data source is inline string` + type: string + secret: + description: Data source is a secret with given Secret + key. + type: string + type: object + clientKey: + description: ClientKey defines a client private key. + properties: + inline: + description: Data source is inline bytes. + format: byte + type: string + inlineString: + description: Data source is inline string` + type: string + secret: + description: Data source is a secret with given Secret + key. + type: string + type: object + mode: + default: Secured + description: Mode defines if proxy should skip verification, + one of `SkipSAN`, `SkipCA`, `Secured`, `SkipAll`. Default + `Secured`. + enum: + - SkipSAN + - SkipCA + - Secured + - SkipAll + type: string + serverName: + description: ServerName overrides the default Server Name + Indicator set by Kuma. + type: string + subjectAltNames: + description: SubjectAltNames list of names to verify in the + certificate. + items: + properties: + type: + default: Exact + description: 'Type specifies matching type, one of `Exact`, + `Prefix`. Default: `Exact`' + enum: + - Exact + - Prefix + type: string + value: + description: Value to match. + type: string + required: + - value + type: object + type: array + type: object + version: + description: Version section for providing version specification. + properties: + max: + default: TLSAuto + description: Max defines maximum supported version. One of + `TLSAuto`, `TLS10`, `TLS11`, `TLS12`, `TLS13`. + enum: + - TLSAuto + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + min: + default: TLSAuto + description: Min defines minimum supported version. One of + `TLSAuto`, `TLS10`, `TLS11`, `TLS12`, `TLS13`. + enum: + - TLSAuto + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + type: object + type: object + required: + - match + type: object + status: + description: Status is the current status of the Kuma MeshExternalService + resource. + properties: + addresses: + description: Addresses section for generated domains + items: + properties: + hostname: + type: string + hostnameGeneratorRef: + properties: + coreName: + type: string + required: + - coreName + type: object + origin: + type: string + type: object + type: array + hostnameGenerators: + items: + properties: + conditions: + description: Conditions is an array of hostname generator conditions. + items: + properties: + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + 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: + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + hostnameGeneratorRef: + properties: + coreName: + type: string + required: + - coreName + type: object + required: + - hostnameGeneratorRef + type: object + type: array + vip: + description: Vip section for allocated IP + properties: + ip: + description: Value allocated IP for a provided domain with `HostnameGenerator` + type in a match section. + type: string + type: object + type: object + type: object + served: true + storage: true diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_meshfaultinjections.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshfaultinjections.yaml new file mode 100644 index 0000000000..e3ccb0b24a --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshfaultinjections.yaml @@ -0,0 +1,419 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: meshfaultinjections.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: MeshFaultInjection + listKind: MeshFaultInjectionList + plural: meshfaultinjections + singular: meshfaultinjection + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.targetRef.kind + name: TargetRef Kind + type: string + - jsonPath: .spec.targetRef.name + name: TargetRef Name + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + 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 is the specification of the Kuma MeshFaultInjection + resource. + properties: + from: + description: From list makes a match between clients and corresponding + configurations + items: + properties: + default: + description: |- + Default is a configuration specific to the group of destinations referenced in + 'targetRef' + properties: + http: + description: Http allows to define list of Http faults between + dataplanes. + items: + description: FaultInjection defines the configuration + of faults between dataplanes. + properties: + abort: + description: |- + Abort defines a configuration of not delivering requests to destination + service and replacing the responses from destination dataplane by + predefined status code + properties: + httpStatus: + description: HTTP status code which will be returned + to source side + format: int32 + type: integer + percentage: + anyOf: + - type: integer + - type: string + description: |- + Percentage of requests on which abort will be injected, has to be + either int or decimal represented as string. + x-kubernetes-int-or-string: true + required: + - httpStatus + - percentage + type: object + delay: + description: Delay defines configuration of delaying + a response from a destination + properties: + percentage: + anyOf: + - type: integer + - type: string + description: |- + Percentage of requests on which delay will be injected, has to be + either int or decimal represented as string. + x-kubernetes-int-or-string: true + value: + description: The duration during which the response + will be delayed + type: string + required: + - percentage + - value + type: object + responseBandwidth: + description: |- + ResponseBandwidth defines a configuration to limit the speed of + responding to the requests + properties: + limit: + description: |- + Limit is represented by value measure in Gbps, Mbps, kbps, e.g. + 10kbps + type: string + percentage: + anyOf: + - type: integer + - type: string + description: |- + Percentage of requests on which response bandwidth limit will be + either int or decimal represented as string. + x-kubernetes-int-or-string: true + required: + - limit + - percentage + type: object + type: object + type: array + type: object + targetRef: + description: |- + TargetRef is a reference to the resource that represents a group of + destinations. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify + cross mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + required: + - targetRef + type: object + type: array + targetRef: + description: |- + TargetRef is a reference to the resource the policy takes an effect on. + The resource could be either a real store object or virtual resource + defined inplace. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify cross + mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + to: + description: To list makes a match between clients and corresponding + configurations + items: + properties: + default: + description: |- + Default is a configuration specific to the group of destinations referenced in + 'targetRef' + properties: + http: + description: Http allows to define list of Http faults between + dataplanes. + items: + description: FaultInjection defines the configuration + of faults between dataplanes. + properties: + abort: + description: |- + Abort defines a configuration of not delivering requests to destination + service and replacing the responses from destination dataplane by + predefined status code + properties: + httpStatus: + description: HTTP status code which will be returned + to source side + format: int32 + type: integer + percentage: + anyOf: + - type: integer + - type: string + description: |- + Percentage of requests on which abort will be injected, has to be + either int or decimal represented as string. + x-kubernetes-int-or-string: true + required: + - httpStatus + - percentage + type: object + delay: + description: Delay defines configuration of delaying + a response from a destination + properties: + percentage: + anyOf: + - type: integer + - type: string + description: |- + Percentage of requests on which delay will be injected, has to be + either int or decimal represented as string. + x-kubernetes-int-or-string: true + value: + description: The duration during which the response + will be delayed + type: string + required: + - percentage + - value + type: object + responseBandwidth: + description: |- + ResponseBandwidth defines a configuration to limit the speed of + responding to the requests + properties: + limit: + description: |- + Limit is represented by value measure in Gbps, Mbps, kbps, e.g. + 10kbps + type: string + percentage: + anyOf: + - type: integer + - type: string + description: |- + Percentage of requests on which response bandwidth limit will be + either int or decimal represented as string. + x-kubernetes-int-or-string: true + required: + - limit + - percentage + type: object + type: object + type: array + type: object + targetRef: + description: |- + TargetRef is a reference to the resource that represents a group of + destinations. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify + cross mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + required: + - targetRef + type: object + type: array + required: + - targetRef + type: object + type: object + served: true + storage: true + subresources: {} diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_meshgatewayinstances.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshgatewayinstances.yaml new file mode 100644 index 0000000000..afa0c47895 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshgatewayinstances.yaml @@ -0,0 +1,364 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: meshgatewayinstances.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: MeshGatewayInstance + listKind: MeshGatewayInstanceList + plural: meshgatewayinstances + singular: meshgatewayinstance + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + MeshGatewayInstance represents a managed instance of a dataplane proxy for a Kuma + Gateway. + 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: MeshGatewayInstanceSpec specifies the options available for + a GatewayDataplane. + properties: + podTemplate: + description: PodTemplate configures the Pod owned by this config. + properties: + metadata: + description: Metadata holds metadata configuration for a Service. + properties: + annotations: + additionalProperties: + type: string + description: Annotations holds annotations to be set on an + object. + type: object + labels: + additionalProperties: + type: string + description: Labels holds labels to be set on an objects. + type: object + type: object + spec: + description: Spec holds some customizable fields of a Pod. + properties: + container: + description: Container corresponds to PodSpec.Container + properties: + securityContext: + description: ContainerSecurityContext corresponds to PodSpec.Container.SecurityContext + properties: + readOnlyRootFilesystem: + description: ReadOnlyRootFilesystem corresponds to + PodSpec.Container.SecurityContext.ReadOnlyRootFilesystem + type: boolean + type: object + type: object + securityContext: + description: PodSecurityContext corresponds to PodSpec.SecurityContext + properties: + fsGroup: + description: FSGroup corresponds to PodSpec.SecurityContext.FSGroup + format: int64 + type: integer + type: object + serviceAccountName: + description: ServiceAccountName corresponds to PodSpec.ServiceAccountName. + type: string + type: object + type: object + replicas: + default: 1 + description: |- + Replicas is the number of dataplane proxy replicas to create. For + now this is a fixed number, but in the future it could be + automatically scaled based on metrics. + format: int32 + minimum: 1 + type: integer + resources: + description: |- + Resources specifies the compute resources for the proxy container. + The default can be set in the control plane config. + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + serviceTemplate: + description: ServiceTemplate configures the Service owned by this + config. + properties: + metadata: + description: Metadata holds metadata configuration for a Service. + properties: + annotations: + additionalProperties: + type: string + description: Annotations holds annotations to be set on an + object. + type: object + labels: + additionalProperties: + type: string + description: Labels holds labels to be set on an objects. + type: object + type: object + spec: + description: Spec holds some customizable fields of a Service. + properties: + loadBalancerIP: + description: LoadBalancerIP corresponds to ServiceSpec.LoadBalancerIP. + type: string + type: object + type: object + serviceType: + default: LoadBalancer + description: |- + ServiceType specifies the type of managed Service that will be + created to expose the dataplane proxies to traffic from outside + the cluster. The ports to expose will be taken from the matching Gateway + resource. If there is no matching Gateway, the managed Service will + be deleted. + enum: + - LoadBalancer + - ClusterIP + - NodePort + type: string + tags: + additionalProperties: + type: string + description: |- + Tags specifies the Kuma tags that are propagated to the managed + dataplane proxies. These tags should include exactly one + `kuma.io/service` tag, and should match exactly one Gateway + resource. + type: object + type: object + status: + description: |- + MeshGatewayInstanceStatus holds information about the status of the gateway + instance. + properties: + conditions: + description: Conditions is an array of gateway instance conditions. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + loadBalancer: + description: |- + LoadBalancer contains the current status of the load-balancer, + if one is present. + properties: + ingress: + description: |- + Ingress is a list containing ingress points for the load-balancer. + Traffic intended for the service should be sent to these ingress points. + items: + description: |- + LoadBalancerIngress represents the status of a load-balancer ingress point: + traffic intended for the service should be sent to an ingress point. + properties: + hostname: + description: |- + Hostname is set for load-balancer ingress points that are DNS based + (typically AWS load-balancers) + type: string + ip: + description: |- + IP is set for load-balancer ingress points that are IP based + (typically GCE or OpenStack load-balancers) + type: string + ipMode: + description: |- + IPMode specifies how the load-balancer IP behaves, and may only be specified when the ip field is specified. + Setting this to "VIP" indicates that traffic is delivered to the node with + the destination set to the load-balancer's IP and port. + Setting this to "Proxy" indicates that traffic is delivered to the node or pod with + the destination set to the node's IP and node port or the pod's IP and port. + Service implementations may use this information to adjust traffic routing. + type: string + ports: + description: |- + Ports is a list of records of service ports + If used, every port defined in the service should have an entry in it + items: + properties: + error: + description: |- + Error is to record the problem with the service port + The format of the error shall comply with the following rules: + - built-in error values shall be specified in this file and those shall use + CamelCase names + - cloud provider specific error values must have names that comply with the + format foo.example.com/CamelCase. + --- + 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 + port: + description: Port is the port number of the service + port of which status is recorded here + format: int32 + type: integer + protocol: + default: TCP + description: |- + Protocol is the protocol of the service port of which status is recorded here + The supported values are: "TCP", "UDP", "SCTP" + type: string + required: + - port + - protocol + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: array + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_meshgatewayroutes.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshgatewayroutes.yaml new file mode 100644 index 0000000000..15156ae47e --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshgatewayroutes.yaml @@ -0,0 +1,50 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: meshgatewayroutes.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: MeshGatewayRoute + listKind: MeshGatewayRouteList + plural: meshgatewayroutes + singular: meshgatewayroute + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + 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 + mesh: + description: |- + Mesh is the name of the Kuma mesh this resource belongs to. + It may be omitted for cluster-scoped resources. + type: string + metadata: + type: object + spec: + description: Spec is the specification of the Kuma MeshGatewayRoute resource. + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: true diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_meshgateways.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshgateways.yaml new file mode 100644 index 0000000000..5ec1b42675 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshgateways.yaml @@ -0,0 +1,50 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: meshgateways.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: MeshGateway + listKind: MeshGatewayList + plural: meshgateways + singular: meshgateway + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + 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 + mesh: + description: |- + Mesh is the name of the Kuma mesh this resource belongs to. + It may be omitted for cluster-scoped resources. + type: string + metadata: + type: object + spec: + description: Spec is the specification of the Kuma MeshGateway resource. + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: true diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_meshhealthchecks.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshhealthchecks.yaml new file mode 100644 index 0000000000..f941d27e01 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshhealthchecks.yaml @@ -0,0 +1,382 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: meshhealthchecks.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: MeshHealthCheck + listKind: MeshHealthCheckList + plural: meshhealthchecks + singular: meshhealthcheck + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.targetRef.kind + name: TargetRef Kind + type: string + - jsonPath: .spec.targetRef.name + name: TargetRef Name + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + 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 is the specification of the Kuma MeshHealthCheck resource. + properties: + targetRef: + description: |- + TargetRef is a reference to the resource the policy takes an effect on. + The resource could be either a real store object or virtual resource + defined inplace. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify cross + mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + to: + description: To list makes a match between the consumed services and + corresponding configurations + items: + properties: + default: + description: |- + Default is a configuration specific to the group of destinations referenced in + 'targetRef' + properties: + alwaysLogHealthCheckFailures: + description: |- + If set to true, health check failure events will always be logged. If set + to false, only the initial health check failure event will be logged. The + default value is false. + type: boolean + eventLogPath: + description: |- + Specifies the path to the file where Envoy can log health check events. + If empty, no event log will be written. + type: string + failTrafficOnPanic: + description: |- + If set to true, Envoy will not consider any hosts when the cluster is in + 'panic mode'. Instead, the cluster will fail all requests as if all hosts + are unhealthy. This can help avoid potentially overwhelming a failing + service. + type: boolean + grpc: + description: |- + GrpcHealthCheck defines gRPC configuration which will instruct the service + the health check will be made for is a gRPC service. + properties: + authority: + description: |- + The value of the :authority header in the gRPC health check request, + by default name of the cluster this health check is associated with + type: string + disabled: + description: If true the GrpcHealthCheck is disabled + type: boolean + serviceName: + description: Service name parameter which will be sent + to gRPC service + type: string + type: object + healthyPanicThreshold: + anyOf: + - type: integer + - type: string + description: |- + Allows to configure panic threshold for Envoy cluster. If not specified, + the default is 50%. To disable panic mode, set to 0%. + Either int or decimal represented as string. + x-kubernetes-int-or-string: true + healthyThreshold: + default: 1 + description: Number of consecutive healthy checks before + considering a host healthy. + format: int32 + type: integer + http: + description: |- + HttpHealthCheck defines HTTP configuration which will instruct the service + the health check will be made for is an HTTP service. + properties: + disabled: + description: If true the HttpHealthCheck is disabled + type: boolean + expectedStatuses: + description: List of HTTP response statuses which are + considered healthy + items: + format: int32 + type: integer + type: array + path: + default: / + description: |- + The HTTP path which will be requested during the health check + (ie. /health) + type: string + requestHeadersToAdd: + description: |- + The list of HTTP headers which should be added to each health check + request + properties: + add: + items: + properties: + name: + maxLength: 256 + minLength: 1 + pattern: ^[a-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + set: + items: + properties: + name: + maxLength: 256 + minLength: 1 + pattern: ^[a-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + 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: object + initialJitter: + description: |- + If specified, Envoy will start health checking after a random time in + ms between 0 and initialJitter. This only applies to the first health + check. + type: string + interval: + default: 1m + description: Interval between consecutive health checks. + type: string + intervalJitter: + description: |- + If specified, during every interval Envoy will add IntervalJitter to the + wait time. + type: string + intervalJitterPercent: + description: |- + If specified, during every interval Envoy will add IntervalJitter * + IntervalJitterPercent / 100 to the wait time. If IntervalJitter and + IntervalJitterPercent are both set, both of them will be used to + increase the wait time. + format: int32 + type: integer + noTrafficInterval: + description: |- + The "no traffic interval" is a special health check interval that is used + when a cluster has never had traffic routed to it. This lower interval + allows cluster information to be kept up to date, without sending a + potentially large amount of active health checking traffic for no reason. + Once a cluster has been used for traffic routing, Envoy will shift back + to using the standard health check interval that is defined. Note that + this interval takes precedence over any other. The default value for "no + traffic interval" is 60 seconds. + type: string + reuseConnection: + description: Reuse health check connection between health + checks. Default is true. + type: boolean + tcp: + description: |- + TcpHealthCheck defines configuration for specifying bytes to send and + expected response during the health check + properties: + disabled: + description: If true the TcpHealthCheck is disabled + type: boolean + receive: + description: |- + List of Base64 encoded blocks of strings expected as a response. When checking the response, + "fuzzy" matching is performed such that each block must be found, and + in the order specified, but not necessarily contiguous. + If not provided or empty, checks will be performed as "connect only" and be marked as successful when TCP connection is successfully established. + items: + type: string + type: array + send: + description: Base64 encoded content of the message which + will be sent during the health check to the target + type: string + type: object + timeout: + default: 15s + description: Maximum time to wait for a health check response. + type: string + unhealthyThreshold: + default: 5 + description: |- + Number of consecutive unhealthy checks before considering a host + unhealthy. + format: int32 + type: integer + type: object + targetRef: + description: |- + TargetRef is a reference to the resource that represents a group of + destinations. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify + cross mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + required: + - targetRef + type: object + type: array + required: + - targetRef + type: object + type: object + served: true + storage: true + subresources: {} diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_meshhttproutes.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshhttproutes.yaml new file mode 100644 index 0000000000..f4dc4952bb --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshhttproutes.yaml @@ -0,0 +1,664 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: meshhttproutes.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: MeshHTTPRoute + listKind: MeshHTTPRouteList + plural: meshhttproutes + singular: meshhttproute + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.targetRef.kind + name: TargetRef Kind + type: string + - jsonPath: .spec.targetRef.name + name: TargetRef Name + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + 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 is the specification of the Kuma MeshHTTPRoute resource. + properties: + targetRef: + description: |- + TargetRef is a reference to the resource the policy takes an effect on. + The resource could be either a real store object or virtual resource + defined inplace. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify cross + mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + to: + description: To matches destination services of requests and holds + configuration. + items: + properties: + hostnames: + description: |- + Hostnames is only valid when targeting MeshGateway and limits the + effects of the rules to requests to this hostname. + Given hostnames must intersect with the hostname of the listeners the + route attaches to. + items: + type: string + type: array + rules: + description: |- + Rules contains the routing rules applies to a combination of top-level + targetRef and the targetRef in this entry. + items: + properties: + default: + description: |- + Default holds routing rules that can be merged with rules from other + policies. + properties: + backendRefs: + items: + description: BackendRef defines where to forward + traffic. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use + to identify cross mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + port: + description: Port is only supported when this + ref refers to a real MeshService object + format: int32 + type: integer + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + weight: + default: 1 + minimum: 0 + type: integer + type: object + type: array + filters: + items: + properties: + requestHeaderModifier: + description: |- + Only one action is supported per header name. + Configuration to set or add multiple values for a header must use RFC 7230 + header value formatting, separating each value with a comma. + properties: + add: + items: + properties: + name: + maxLength: 256 + minLength: 1 + pattern: ^[a-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + items: + type: string + maxItems: 16 + type: array + set: + items: + properties: + name: + maxLength: 256 + minLength: 1 + pattern: ^[a-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + 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: + properties: + backendRef: + description: TODO forbid weight + properties: + kind: + description: Kind of the referenced + resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future + use to identify cross mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + port: + description: Port is only supported + when this ref refers to a real MeshService + object + format: int32 + type: integer + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + weight: + default: 1 + minimum: 0 + type: integer + type: object + percentage: + anyOf: + - type: integer + - type: string + description: |- + Percentage of requests to mirror. If not specified, all requests + to the target cluster will be mirrored. + x-kubernetes-int-or-string: true + required: + - backendRef + type: object + requestRedirect: + properties: + hostname: + description: |- + PreciseHostname is the fully qualified domain name of a network host. This + matches the RFC 1123 definition of a hostname with 1 notable exception that + numeric IP addresses are not allowed. + + + 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 + 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. + properties: + replaceFullPath: + type: string + replacePrefixMatch: + type: string + type: + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: |- + Port is the port to be used in the value of the `Location` + header in the response. + When empty, port (if specified) of the request is used. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + enum: + - http + - https + type: string + statusCode: + default: 302 + description: StatusCode is the HTTP status + code to be used in response. + enum: + - 301 + - 302 + - 303 + - 307 + - 308 + type: integer + type: object + responseHeaderModifier: + description: |- + Only one action is supported per header name. + Configuration to set or add multiple values for a header must use RFC 7230 + header value formatting, separating each value with a comma. + properties: + add: + items: + properties: + name: + maxLength: 256 + minLength: 1 + pattern: ^[a-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + items: + type: string + maxItems: 16 + type: array + set: + items: + properties: + name: + maxLength: 256 + minLength: 1 + pattern: ^[a-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + 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: + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestRedirect + - URLRewrite + - RequestMirror + type: string + urlRewrite: + properties: + hostToBackendHostname: + description: |- + HostToBackendHostname rewrites the hostname to the hostname of the + upstream host. This option is only available when targeting MeshGateways. + type: boolean + hostname: + description: Hostname is the value to be + used to replace the host header value + during forwarding. + 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. + properties: + replaceFullPath: + type: string + replacePrefixMatch: + type: string + type: + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + type: array + type: object + matches: + description: |- + Matches describes how to match HTTP requests this rule should be applied + to. + items: + properties: + headers: + items: + description: |- + HeaderMatch describes how to select an HTTP route by matching HTTP request + headers. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name MUST be lower case + as they will be handled with case insensitivity (See https://tools.ietf.org/html/rfc7230#section-3.2). + maxLength: 256 + minLength: 1 + pattern: ^[a-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: Type specifies how to match against + the value of the header. + enum: + - Exact + - Present + - RegularExpression + - Absent + - Prefix + type: string + value: + description: Value is the value of HTTP Header + to be matched. + type: string + required: + - name + type: object + type: array + method: + enum: + - CONNECT + - DELETE + - GET + - HEAD + - OPTIONS + - PATCH + - POST + - PUT + - TRACE + type: string + path: + properties: + type: + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + description: |- + Exact or prefix matches must be an absolute path. A prefix matches only + if separated by a slash or the entire path. + minLength: 1 + type: string + required: + - type + - value + type: object + queryParams: + description: |- + QueryParams matches based on HTTP URL query parameters. Multiple matches + are ANDed together such that all listed matches must succeed. + items: + properties: + name: + minLength: 1 + type: string + type: + enum: + - Exact + - RegularExpression + type: string + value: + type: string + required: + - name + - type + - value + type: object + type: array + type: object + minItems: 1 + type: array + required: + - default + - matches + type: object + type: array + targetRef: + description: |- + TargetRef is a reference to the resource that represents a group of + request destinations. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify + cross mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: {} diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_meshinsights.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshinsights.yaml new file mode 100644 index 0000000000..c72f08ed9d --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshinsights.yaml @@ -0,0 +1,50 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: meshinsights.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: MeshInsight + listKind: MeshInsightList + plural: meshinsights + singular: meshinsight + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + 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 + mesh: + description: |- + Mesh is the name of the Kuma mesh this resource belongs to. + It may be omitted for cluster-scoped resources. + type: string + metadata: + type: object + spec: + description: Spec is the specification of the Kuma MeshInsight resource. + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: true diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_meshloadbalancingstrategies.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshloadbalancingstrategies.yaml new file mode 100644 index 0000000000..38fd712fca --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshloadbalancingstrategies.yaml @@ -0,0 +1,572 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: meshloadbalancingstrategies.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: MeshLoadBalancingStrategy + listKind: MeshLoadBalancingStrategyList + plural: meshloadbalancingstrategies + singular: meshloadbalancingstrategy + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.targetRef.kind + name: TargetRef Kind + type: string + - jsonPath: .spec.targetRef.name + name: TargetRef Name + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + 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 is the specification of the Kuma MeshLoadBalancingStrategy + resource. + properties: + targetRef: + description: |- + TargetRef is a reference to the resource the policy takes an effect on. + The resource could be either a real store object or virtual resource + defined inplace. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify cross + mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + to: + description: To list makes a match between the consumed services and + corresponding configurations + items: + properties: + default: + description: |- + Default is a configuration specific to the group of destinations referenced in + 'targetRef' + properties: + loadBalancer: + description: LoadBalancer allows to specify load balancing + algorithm. + properties: + leastRequest: + description: |- + LeastRequest selects N random available hosts as specified in 'choiceCount' (2 by default) + and picks the host which has the fewest active requests + properties: + activeRequestBias: + anyOf: + - type: integer + - type: string + description: |- + ActiveRequestBias refers to dynamic weights applied when hosts have varying load + balancing weights. A higher value here aggressively reduces the weight of endpoints + that are currently handling active requests. In essence, the higher the ActiveRequestBias + value, the more forcefully it reduces the load balancing weight of endpoints that are + actively serving requests. + x-kubernetes-int-or-string: true + choiceCount: + description: |- + ChoiceCount is the number of random healthy hosts from which the host with + the fewest active requests will be chosen. Defaults to 2 so that Envoy performs + two-choice selection if the field is not set. + format: int32 + minimum: 2 + type: integer + type: object + maglev: + description: |- + Maglev implements consistent hashing to upstream hosts. Maglev can be used as + a drop in replacement for the ring hash load balancer any place in which + consistent hashing is desired. + properties: + hashPolicies: + description: |- + HashPolicies specify a list of request/connection properties that are used to calculate a hash. + These hash policies are executed in the specified order. If a hash policy has the “terminal” attribute + set to true, and there is already a hash generated, the hash is returned immediately, + ignoring the rest of the hash policy list. + items: + properties: + connection: + properties: + sourceIP: + description: Hash on source IP address. + type: boolean + type: object + cookie: + properties: + name: + description: The name of the cookie that + will be used to obtain the hash key. + minLength: 1 + type: string + path: + description: The name of the path for + the cookie. + type: string + ttl: + description: If specified, a cookie with + the TTL will be generated if the cookie + is not present. + type: string + required: + - name + type: object + filterState: + properties: + key: + description: |- + The name of the Object in the per-request filterState, which is + an Envoy::Hashable object. If there is no data associated with the key, + or the stored object is not Envoy::Hashable, no hash will be produced. + minLength: 1 + type: string + required: + - key + type: object + header: + properties: + name: + description: The name of the request header + that will be used to obtain the hash + key. + minLength: 1 + type: string + required: + - name + type: object + queryParameter: + properties: + name: + description: |- + The name of the URL query parameter that will be used to obtain the hash key. + If the parameter is not present, no hash will be produced. Query parameter names + are case-sensitive. + minLength: 1 + type: string + required: + - name + type: object + terminal: + description: |- + Terminal is a flag that short-circuits the hash computing. This field provides + a ‘fallback’ style of configuration: “if a terminal policy doesn’t work, fallback + to rest of the policy list”, it saves time when the terminal policy works. + If true, and there is already a hash computed, ignore rest of the list of hash polices. + type: boolean + type: + enum: + - Header + - Cookie + - SourceIP + - QueryParameter + - FilterState + type: string + required: + - type + type: object + type: array + tableSize: + description: |- + The table size for Maglev hashing. Maglev aims for “minimal disruption” + rather than an absolute guarantee. Minimal disruption means that when + the set of upstream hosts change, a connection will likely be sent + to the same upstream as it was before. Increasing the table size reduces + the amount of disruption. The table size must be prime number limited to 5000011. + If it is not specified, the default is 65537. + format: int32 + maximum: 5000011 + minimum: 1 + type: integer + type: object + random: + description: |- + Random selects a random available host. The random load balancer generally + performs better than round-robin if no health checking policy is configured. + Random selection avoids bias towards the host in the set that comes after a failed host. + type: object + ringHash: + description: |- + RingHash implements consistent hashing to upstream hosts. Each host is mapped + onto a circle (the “ring”) by hashing its address; each request is then routed + to a host by hashing some property of the request, and finding the nearest + corresponding host clockwise around the ring. + properties: + hashFunction: + description: |- + HashFunction is a function used to hash hosts onto the ketama ring. + The value defaults to XX_HASH. Available values – XX_HASH, MURMUR_HASH_2. + enum: + - XXHash + - MurmurHash2 + type: string + hashPolicies: + description: |- + HashPolicies specify a list of request/connection properties that are used to calculate a hash. + These hash policies are executed in the specified order. If a hash policy has the “terminal” attribute + set to true, and there is already a hash generated, the hash is returned immediately, + ignoring the rest of the hash policy list. + items: + properties: + connection: + properties: + sourceIP: + description: Hash on source IP address. + type: boolean + type: object + cookie: + properties: + name: + description: The name of the cookie that + will be used to obtain the hash key. + minLength: 1 + type: string + path: + description: The name of the path for + the cookie. + type: string + ttl: + description: If specified, a cookie with + the TTL will be generated if the cookie + is not present. + type: string + required: + - name + type: object + filterState: + properties: + key: + description: |- + The name of the Object in the per-request filterState, which is + an Envoy::Hashable object. If there is no data associated with the key, + or the stored object is not Envoy::Hashable, no hash will be produced. + minLength: 1 + type: string + required: + - key + type: object + header: + properties: + name: + description: The name of the request header + that will be used to obtain the hash + key. + minLength: 1 + type: string + required: + - name + type: object + queryParameter: + properties: + name: + description: |- + The name of the URL query parameter that will be used to obtain the hash key. + If the parameter is not present, no hash will be produced. Query parameter names + are case-sensitive. + minLength: 1 + type: string + required: + - name + type: object + terminal: + description: |- + Terminal is a flag that short-circuits the hash computing. This field provides + a ‘fallback’ style of configuration: “if a terminal policy doesn’t work, fallback + to rest of the policy list”, it saves time when the terminal policy works. + If true, and there is already a hash computed, ignore rest of the list of hash polices. + type: boolean + type: + enum: + - Header + - Cookie + - SourceIP + - QueryParameter + - FilterState + type: string + required: + - type + type: object + type: array + maxRingSize: + description: |- + Maximum hash ring size. Defaults to 8M entries, and limited to 8M entries, + but can be lowered to further constrain resource use. + format: int32 + maximum: 8000000 + minimum: 1 + type: integer + minRingSize: + description: |- + Minimum hash ring size. The larger the ring is (that is, + the more hashes there are for each provided host) the better the request distribution + will reflect the desired weights. Defaults to 1024 entries, and limited to 8M entries. + format: int32 + maximum: 8000000 + minimum: 1 + type: integer + type: object + roundRobin: + description: |- + RoundRobin is a load balancing algorithm that distributes requests + across available upstream hosts in round-robin order. + type: object + type: + enum: + - RoundRobin + - LeastRequest + - RingHash + - Random + - Maglev + type: string + required: + - type + type: object + localityAwareness: + description: LocalityAwareness contains configuration for + locality aware load balancing. + properties: + crossZone: + description: |- + CrossZone defines locality aware load balancing priorities when dataplane proxies inside local zone + are unavailable + properties: + failover: + description: Failover defines list of load balancing + rules in order of priority + items: + properties: + from: + description: From defines the list of zones + to which the rule applies + properties: + zones: + items: + type: string + type: array + required: + - zones + type: object + to: + description: To defines to which zones the + traffic should be load balanced + properties: + type: + description: Type defines how target zones + will be picked from available zones + enum: + - None + - Only + - Any + - AnyExcept + type: string + zones: + items: + type: string + type: array + required: + - type + type: object + required: + - to + type: object + type: array + failoverThreshold: + description: |- + FailoverThreshold defines the percentage of live destination dataplane proxies below which load balancing to the + next priority starts. + Example: If you configure failoverThreshold to 70, and you have deployed 10 destination dataplane proxies. + Load balancing to next priority will start when number of live destination dataplane proxies drops below 7. + Default 50 + properties: + percentage: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - percentage + type: object + type: object + disabled: + description: |- + Disabled allows to disable locality-aware load balancing. + When disabled requests are distributed across all endpoints regardless of locality. + type: boolean + localZone: + description: LocalZone defines locality aware load balancing + priorities between dataplane proxies inside a zone + properties: + affinityTags: + description: AffinityTags list of tags for local + zone load balancing. + items: + properties: + key: + description: Key defines tag for which affinity + is configured + type: string + weight: + description: |- + Weight of the tag used for load balancing. The bigger the weight the bigger the priority. + Percentage of local traffic load balanced to tag is computed by dividing weight by sum of weights from all tags. + For example with two affinity tags first with weight 80 and second with weight 20, + then 80% of traffic will be redirected to the first tag, and 20% of traffic will be redirected to second one. + Setting weights is not mandatory. When weights are not set control plane will compute default weight based on list order. + Default: If you do not specify weight we will adjust them so that 90% traffic goes to first tag, 9% to next, and 1% to third and so on. + format: int32 + type: integer + required: + - key + type: object + type: array + type: object + type: object + type: object + targetRef: + description: |- + TargetRef is a reference to the resource that represents a group of + destinations. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify + cross mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + required: + - targetRef + type: object + type: array + required: + - targetRef + type: object + type: object + served: true + storage: true + subresources: {} diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_meshmetrics.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshmetrics.yaml new file mode 100644 index 0000000000..260f6916e3 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshmetrics.yaml @@ -0,0 +1,293 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: meshmetrics.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: MeshMetric + listKind: MeshMetricList + plural: meshmetrics + singular: meshmetric + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.targetRef.kind + name: TargetRef Kind + type: string + - jsonPath: .spec.targetRef.name + name: TargetRef Name + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + 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 is the specification of the Kuma MeshMetric resource. + properties: + default: + description: MeshMetric configuration. + properties: + applications: + description: Applications is a list of application that Dataplane + Proxy will scrape + items: + properties: + address: + description: Address on which an application listens. + type: string + name: + description: Name of the application to scrape + type: string + path: + default: /metrics/prometheus + description: Path on which an application expose HTTP endpoint + with metrics. + type: string + port: + description: Port on which an application expose HTTP endpoint + with metrics. + format: int32 + type: integer + required: + - port + type: object + type: array + backends: + description: Backends list that will be used to collect metrics. + items: + properties: + openTelemetry: + description: OpenTelemetry backend configuration + properties: + endpoint: + description: Endpoint for OpenTelemetry collector + type: string + refreshInterval: + description: RefreshInterval defines how frequent metrics + should be pushed to collector + type: string + required: + - endpoint + type: object + prometheus: + description: Prometheus backend configuration. + properties: + clientId: + description: ClientId of the Prometheus backend. Needed + when using MADS for DP discovery. + type: string + path: + default: /metrics + description: Path on which a dataplane should expose + HTTP endpoint with Prometheus metrics. + type: string + port: + default: 5670 + description: Port on which a dataplane should expose + HTTP endpoint with Prometheus metrics. + format: int32 + type: integer + tls: + description: Configuration of TLS for prometheus listener. + properties: + mode: + default: Disabled + description: Configuration of TLS for Prometheus + listener. + enum: + - Disabled + - ProvidedTLS + - ActiveMTLSBackend + type: string + required: + - mode + type: object + required: + - path + - port + type: object + type: + description: Type of the backend that will be used to collect + metrics. At the moment only Prometheus backend is available. + enum: + - Prometheus + - OpenTelemetry + type: string + required: + - type + type: object + type: array + sidecar: + description: Sidecar metrics collection configuration + properties: + includeUnused: + default: false + description: |- + IncludeUnused if false will scrape only metrics that has been by sidecar (counters incremented + at least once, gauges changed at least once, and histograms added to at + least once). If true will scrape all metrics (even the ones with zeros). + type: boolean + profiles: + description: Profiles allows to customize which metrics are + published. + properties: + appendProfiles: + description: AppendProfiles allows to combine the metrics + from multiple predefined profiles. + items: + properties: + name: + description: 'Name of the predefined profile, one + of: all, basic, none' + enum: + - All + - Basic + - None + type: string + required: + - name + type: object + type: array + exclude: + description: |- + Exclude makes it possible to exclude groups of metrics from a resulting profile. + Exclude is subordinate to Include. + items: + properties: + match: + description: Match is the value used to match using + particular Type + type: string + type: + description: 'Type defined the type of selector, + one of: prefix, regex, exact' + enum: + - Prefix + - Regex + - Exact + - Contains + type: string + required: + - match + - type + type: object + type: array + include: + description: |- + Include makes it possible to include additional metrics in a selected profiles. + Include takes precedence over Exclude. + items: + properties: + match: + description: Match is the value used to match using + particular Type + type: string + type: + description: 'Type defined the type of selector, + one of: prefix, regex, exact' + enum: + - Prefix + - Regex + - Exact + - Contains + type: string + required: + - match + - type + type: object + type: array + type: object + type: object + type: object + targetRef: + description: |- + TargetRef is a reference to the resource the policy takes an effect on. + The resource could be either a real store object or virtual resource + defined in-place. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify cross + mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + required: + - targetRef + type: object + type: object + served: true + storage: true + subresources: {} diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_meshpassthroughs.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshpassthroughs.yaml new file mode 100644 index 0000000000..aaa17e47e5 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshpassthroughs.yaml @@ -0,0 +1,167 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: meshpassthroughs.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: MeshPassthrough + listKind: MeshPassthroughList + plural: meshpassthroughs + singular: meshpassthrough + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.targetRef.kind + name: TargetRef Kind + type: string + - jsonPath: .spec.targetRef.name + name: TargetRef Name + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + 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 is the specification of the Kuma MeshPassthrough resource. + properties: + default: + description: MeshPassthrough configuration. + properties: + appendMatch: + description: AppendMatch is a list of destinations that should + be allowed through the sidecar. + items: + properties: + port: + description: Port defines the port to which a user makes + a request. + type: integer + protocol: + default: tcp + description: 'Protocol defines the communication protocol. + Possible values: `tcp`, `tls`, `grpc`, `http`, `http2`.' + enum: + - tcp + - tls + - grpc + - http + - http2 + type: string + type: + description: Type of the match, one of `Domain`, `IP` or + `CIDR` is available. + enum: + - Domain + - IP + - CIDR + type: string + value: + description: Value for the specified Type. + type: string + required: + - port + type: object + type: array + passthroughMode: + default: None + description: |- + Defines the passthrough behavior. Possible values: `All`, `None`, `Matched` + When `All` or `None` `appendMatch` has no effect. + enum: + - All + - Matched + - None + type: string + type: object + targetRef: + description: |- + TargetRef is a reference to the resource the policy takes an effect on. + The resource could be either a real store object or virtual resource + defined in-place. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify cross + mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + required: + - targetRef + type: object + type: object + served: true + storage: true + subresources: {} diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_meshproxypatches.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshproxypatches.yaml new file mode 100644 index 0000000000..76daf5a47b --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshproxypatches.yaml @@ -0,0 +1,560 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: meshproxypatches.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: MeshProxyPatch + listKind: MeshProxyPatchList + plural: meshproxypatches + singular: meshproxypatch + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.targetRef.kind + name: TargetRef Kind + type: string + - jsonPath: .spec.targetRef.name + name: TargetRef Name + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + 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 is the specification of the Kuma MeshProxyPatch resource. + properties: + default: + description: |- + Default is a configuration specific to the group of destinations + referenced in 'targetRef'. + properties: + appendModifications: + description: AppendModifications is a list of modifications applied + on the selected proxy. + items: + properties: + cluster: + description: Cluster is a modification of Envoy's Cluster + resource. + properties: + jsonPatches: + description: |- + JsonPatches specifies list of jsonpatches to apply to on Envoy's Cluster + resource + items: + description: JsonPatchBlock is one json patch operation + block. + properties: + from: + description: From is a jsonpatch from string, + used by move and copy operations. + type: string + op: + description: Op is a jsonpatch operation string. + enum: + - add + - remove + - replace + - move + - copy + type: string + path: + description: Path is a jsonpatch path string. + type: string + value: + description: Value must be a valid json value + used by replace and add operations. + x-kubernetes-preserve-unknown-fields: true + required: + - op + - path + type: object + type: array + match: + description: Match is a set of conditions that have + to be matched for modification operation to happen. + properties: + name: + description: Name of the cluster to match. + type: string + origin: + description: |- + Origin is the name of the component or plugin that generated the resource. + + + Here is the list of well-known origins: + inbound - resources generated for handling incoming traffic. + outbound - resources generated for handling outgoing traffic. + transparent - resources generated for transparent proxy functionality. + prometheus - resources generated when Prometheus metrics are enabled. + direct-access - resources generated for Direct Access functionality. + ingress - resources generated for Zone Ingress. + egress - resources generated for Zone Egress. + gateway - resources generated for MeshGateway. + + + The list is not complete, because policy plugins can introduce new resources. + For example MeshTrace plugin can create Cluster with "mesh-trace" origin. + type: string + type: object + operation: + description: Operation to execute on matched cluster. + enum: + - Add + - Remove + - Patch + type: string + value: + description: Value of xDS resource in YAML format to + add or patch. + type: string + required: + - operation + type: object + httpFilter: + description: |- + HTTPFilter is a modification of Envoy HTTP Filter + available in HTTP Connection Manager in a Listener resource. + properties: + jsonPatches: + description: |- + JsonPatches specifies list of jsonpatches to apply to on Envoy's + HTTP Filter available in HTTP Connection Manager in a Listener resource. + items: + description: JsonPatchBlock is one json patch operation + block. + properties: + from: + description: From is a jsonpatch from string, + used by move and copy operations. + type: string + op: + description: Op is a jsonpatch operation string. + enum: + - add + - remove + - replace + - move + - copy + type: string + path: + description: Path is a jsonpatch path string. + type: string + value: + description: Value must be a valid json value + used by replace and add operations. + x-kubernetes-preserve-unknown-fields: true + required: + - op + - path + type: object + type: array + match: + description: Match is a set of conditions that have + to be matched for modification operation to happen. + properties: + listenerName: + description: Name of the listener to match. + type: string + listenerTags: + additionalProperties: + type: string + description: Listener tags available in Listener#Metadata#FilterMetadata[io.kuma.tags] + type: object + name: + description: Name of the HTTP filter. For example + "envoy.filters.http.local_ratelimit" + type: string + origin: + description: |- + Origin is the name of the component or plugin that generated the resource. + + + Here is the list of well-known origins: + inbound - resources generated for handling incoming traffic. + outbound - resources generated for handling outgoing traffic. + transparent - resources generated for transparent proxy functionality. + prometheus - resources generated when Prometheus metrics are enabled. + direct-access - resources generated for Direct Access functionality. + ingress - resources generated for Zone Ingress. + egress - resources generated for Zone Egress. + gateway - resources generated for MeshGateway. + + + The list is not complete, because policy plugins can introduce new resources. + For example MeshTrace plugin can create Cluster with "mesh-trace" origin. + type: string + type: object + operation: + description: Operation to execute on matched listener. + enum: + - Remove + - Patch + - AddFirst + - AddBefore + - AddAfter + - AddLast + type: string + value: + description: Value of xDS resource in YAML format to + add or patch. + type: string + required: + - operation + type: object + listener: + description: Listener is a modification of Envoy's Listener + resource. + properties: + jsonPatches: + description: |- + JsonPatches specifies list of jsonpatches to apply to on Envoy's Listener + resource + items: + description: JsonPatchBlock is one json patch operation + block. + properties: + from: + description: From is a jsonpatch from string, + used by move and copy operations. + type: string + op: + description: Op is a jsonpatch operation string. + enum: + - add + - remove + - replace + - move + - copy + type: string + path: + description: Path is a jsonpatch path string. + type: string + value: + description: Value must be a valid json value + used by replace and add operations. + x-kubernetes-preserve-unknown-fields: true + required: + - op + - path + type: object + type: array + match: + description: Match is a set of conditions that have + to be matched for modification operation to happen. + properties: + name: + description: Name of the listener to match. + type: string + origin: + description: |- + Origin is the name of the component or plugin that generated the resource. + + + Here is the list of well-known origins: + inbound - resources generated for handling incoming traffic. + outbound - resources generated for handling outgoing traffic. + transparent - resources generated for transparent proxy functionality. + prometheus - resources generated when Prometheus metrics are enabled. + direct-access - resources generated for Direct Access functionality. + ingress - resources generated for Zone Ingress. + egress - resources generated for Zone Egress. + gateway - resources generated for MeshGateway. + + + The list is not complete, because policy plugins can introduce new resources. + For example MeshTrace plugin can create Cluster with "mesh-trace" origin. + type: string + tags: + additionalProperties: + type: string + description: Tags available in Listener#Metadata#FilterMetadata[io.kuma.tags] + type: object + type: object + operation: + description: Operation to execute on matched listener. + enum: + - Add + - Remove + - Patch + type: string + value: + description: Value of xDS resource in YAML format to + add or patch. + type: string + required: + - operation + type: object + networkFilter: + description: NetworkFilter is a modification of Envoy Listener's + filter. + properties: + jsonPatches: + description: |- + JsonPatches specifies list of jsonpatches to apply to on Envoy Listener's + filter. + items: + description: JsonPatchBlock is one json patch operation + block. + properties: + from: + description: From is a jsonpatch from string, + used by move and copy operations. + type: string + op: + description: Op is a jsonpatch operation string. + enum: + - add + - remove + - replace + - move + - copy + type: string + path: + description: Path is a jsonpatch path string. + type: string + value: + description: Value must be a valid json value + used by replace and add operations. + x-kubernetes-preserve-unknown-fields: true + required: + - op + - path + type: object + type: array + match: + description: Match is a set of conditions that have + to be matched for modification operation to happen. + properties: + listenerName: + description: Name of the listener to match. + type: string + listenerTags: + additionalProperties: + type: string + description: Listener tags available in Listener#Metadata#FilterMetadata[io.kuma.tags] + type: object + name: + description: Name of the network filter. For example + "envoy.filters.network.ratelimit" + type: string + origin: + description: |- + Origin is the name of the component or plugin that generated the resource. + + + Here is the list of well-known origins: + inbound - resources generated for handling incoming traffic. + outbound - resources generated for handling outgoing traffic. + transparent - resources generated for transparent proxy functionality. + prometheus - resources generated when Prometheus metrics are enabled. + direct-access - resources generated for Direct Access functionality. + ingress - resources generated for Zone Ingress. + egress - resources generated for Zone Egress. + gateway - resources generated for MeshGateway. + + + The list is not complete, because policy plugins can introduce new resources. + For example MeshTrace plugin can create Cluster with "mesh-trace" origin. + type: string + type: object + operation: + description: Operation to execute on matched listener. + enum: + - Remove + - Patch + - AddFirst + - AddBefore + - AddAfter + - AddLast + type: string + value: + description: Value of xDS resource in YAML format to + add or patch. + type: string + required: + - operation + type: object + virtualHost: + description: |- + VirtualHost is a modification of Envoy's VirtualHost + referenced in HTTP Connection Manager in a Listener resource. + properties: + jsonPatches: + description: |- + JsonPatches specifies list of jsonpatches to apply to on Envoy's + VirtualHost resource + items: + description: JsonPatchBlock is one json patch operation + block. + properties: + from: + description: From is a jsonpatch from string, + used by move and copy operations. + type: string + op: + description: Op is a jsonpatch operation string. + enum: + - add + - remove + - replace + - move + - copy + type: string + path: + description: Path is a jsonpatch path string. + type: string + value: + description: Value must be a valid json value + used by replace and add operations. + x-kubernetes-preserve-unknown-fields: true + required: + - op + - path + type: object + type: array + match: + description: Match is a set of conditions that have + to be matched for modification operation to happen. + properties: + name: + description: Name of the VirtualHost to match. + type: string + origin: + description: |- + Origin is the name of the component or plugin that generated the resource. + + + Here is the list of well-known origins: + inbound - resources generated for handling incoming traffic. + outbound - resources generated for handling outgoing traffic. + transparent - resources generated for transparent proxy functionality. + prometheus - resources generated when Prometheus metrics are enabled. + direct-access - resources generated for Direct Access functionality. + ingress - resources generated for Zone Ingress. + egress - resources generated for Zone Egress. + gateway - resources generated for MeshGateway. + + + The list is not complete, because policy plugins can introduce new resources. + For example MeshTrace plugin can create Cluster with "mesh-trace" origin. + type: string + routeConfigurationName: + description: Name of the RouteConfiguration resource + to match. + type: string + type: object + operation: + description: Operation to execute on matched listener. + enum: + - Add + - Remove + - Patch + type: string + value: + description: Value of xDS resource in YAML format to + add or patch. + type: string + required: + - match + - operation + type: object + type: object + type: array + required: + - appendModifications + type: object + targetRef: + description: |- + TargetRef is a reference to the resource the policy takes an effect on. + The resource could be either a real store object or virtual resource + defined inplace. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify cross + mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + required: + - default + - targetRef + type: object + type: object + served: true + storage: true + subresources: {} diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_meshratelimits.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshratelimits.yaml new file mode 100644 index 0000000000..844d9c52f7 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshratelimits.yaml @@ -0,0 +1,498 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: meshratelimits.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: MeshRateLimit + listKind: MeshRateLimitList + plural: meshratelimits + singular: meshratelimit + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.targetRef.kind + name: TargetRef Kind + type: string + - jsonPath: .spec.targetRef.name + name: TargetRef Name + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + 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 is the specification of the Kuma MeshRateLimit resource. + properties: + from: + description: From list makes a match between clients and corresponding + configurations + items: + properties: + default: + description: |- + Default is a configuration specific to the group of clients referenced in + 'targetRef' + properties: + local: + description: LocalConf defines local http or/and tcp rate + limit configuration + properties: + http: + description: |- + LocalHTTP defines configuration of local HTTP rate limiting + https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/local_rate_limit_filter + properties: + disabled: + description: Define if rate limiting should be disabled. + type: boolean + onRateLimit: + description: Describes the actions to take on a + rate limit event + properties: + headers: + description: The Headers to be added to the + HTTP response on a rate limit event + properties: + add: + items: + properties: + name: + maxLength: 256 + minLength: 1 + pattern: ^[a-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + set: + items: + properties: + name: + maxLength: 256 + minLength: 1 + pattern: ^[a-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + status: + description: The HTTP status code to be set + on a rate limit event + format: int32 + type: integer + type: object + requestRate: + description: Defines how many requests are allowed + per interval. + properties: + interval: + description: The interval the number of units + is accounted for. + type: string + num: + description: |- + Number of units per interval (depending on usage it can be a number of requests, + or a number of connections). + format: int32 + type: integer + required: + - interval + - num + type: object + type: object + tcp: + description: |- + LocalTCP defines confguration of local TCP rate limiting + https://www.envoyproxy.io/docs/envoy/latest/configuration/listeners/network_filters/local_rate_limit_filter + properties: + connectionRate: + description: Defines how many connections are allowed + per interval. + properties: + interval: + description: The interval the number of units + is accounted for. + type: string + num: + description: |- + Number of units per interval (depending on usage it can be a number of requests, + or a number of connections). + format: int32 + type: integer + required: + - interval + - num + type: object + disabled: + description: |- + Define if rate limiting should be disabled. + Default: false + type: boolean + type: object + type: object + type: object + targetRef: + description: |- + TargetRef is a reference to the resource that represents a group of + clients. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify + cross mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + required: + - targetRef + type: object + type: array + targetRef: + description: |- + TargetRef is a reference to the resource the policy takes an effect on. + The resource could be either a real store object or virtual resource + defined inplace. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify cross + mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + to: + description: To list makes a match between clients and corresponding + configurations + items: + properties: + default: + description: |- + Default is a configuration specific to the group of clients referenced in + 'targetRef' + properties: + local: + description: LocalConf defines local http or/and tcp rate + limit configuration + properties: + http: + description: |- + LocalHTTP defines configuration of local HTTP rate limiting + https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/local_rate_limit_filter + properties: + disabled: + description: Define if rate limiting should be disabled. + type: boolean + onRateLimit: + description: Describes the actions to take on a + rate limit event + properties: + headers: + description: The Headers to be added to the + HTTP response on a rate limit event + properties: + add: + items: + properties: + name: + maxLength: 256 + minLength: 1 + pattern: ^[a-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + set: + items: + properties: + name: + maxLength: 256 + minLength: 1 + pattern: ^[a-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + status: + description: The HTTP status code to be set + on a rate limit event + format: int32 + type: integer + type: object + requestRate: + description: Defines how many requests are allowed + per interval. + properties: + interval: + description: The interval the number of units + is accounted for. + type: string + num: + description: |- + Number of units per interval (depending on usage it can be a number of requests, + or a number of connections). + format: int32 + type: integer + required: + - interval + - num + type: object + type: object + tcp: + description: |- + LocalTCP defines confguration of local TCP rate limiting + https://www.envoyproxy.io/docs/envoy/latest/configuration/listeners/network_filters/local_rate_limit_filter + properties: + connectionRate: + description: Defines how many connections are allowed + per interval. + properties: + interval: + description: The interval the number of units + is accounted for. + type: string + num: + description: |- + Number of units per interval (depending on usage it can be a number of requests, + or a number of connections). + format: int32 + type: integer + required: + - interval + - num + type: object + disabled: + description: |- + Define if rate limiting should be disabled. + Default: false + type: boolean + type: object + type: object + type: object + targetRef: + description: |- + TargetRef is a reference to the resource that represents a group of + clients. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify + cross mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + required: + - targetRef + type: object + type: array + required: + - targetRef + type: object + type: object + served: true + storage: true + subresources: {} diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_meshretries.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshretries.yaml new file mode 100644 index 0000000000..404c0b2e5b --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshretries.yaml @@ -0,0 +1,507 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: meshretries.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: MeshRetry + listKind: MeshRetryList + plural: meshretries + singular: meshretry + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.targetRef.kind + name: TargetRef Kind + type: string + - jsonPath: .spec.targetRef.name + name: TargetRef Name + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + 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 is the specification of the Kuma MeshRetry resource. + properties: + targetRef: + description: |- + TargetRef is a reference to the resource the policy takes an effect on. + The resource could be either a real store object or virtual resource + defined inplace. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify cross + mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + to: + description: To list makes a match between the consumed services and + corresponding configurations + items: + properties: + default: + description: |- + Default is a configuration specific to the group of destinations referenced in + 'targetRef' + properties: + grpc: + description: GRPC defines a configuration of retries for + GRPC traffic + properties: + backOff: + description: |- + BackOff is a configuration of durations which will be used in an exponential + backoff strategy between retries. + properties: + baseInterval: + default: 25ms + description: |- + BaseInterval is an amount of time which should be taken between retries. + Must be greater than zero. Values less than 1 ms are rounded up to 1 ms. + type: string + maxInterval: + description: |- + MaxInterval is a maximal amount of time which will be taken between retries. + Default is 10 times the "BaseInterval". + type: string + type: object + numRetries: + description: |- + NumRetries is the number of attempts that will be made on failed (and + retriable) requests. If not set, the default value is 1. + format: int32 + type: integer + perTryTimeout: + description: |- + PerTryTimeout is the maximum amount of time each retry attempt can take + before it times out. If not set, the global request timeout for the route + will be used. Setting this value to 0 will disable the per-try timeout. + type: string + rateLimitedBackOff: + description: |- + RateLimitedBackOff is a configuration of backoff which will be used when + the upstream returns one of the headers configured. + properties: + maxInterval: + default: 300s + description: MaxInterval is a maximal amount of + time which will be taken between retries. + type: string + resetHeaders: + description: |- + ResetHeaders specifies the list of headers (like Retry-After or X-RateLimit-Reset) + to match against the response. Headers are tried in order, and matched + case-insensitive. The first header to be parsed successfully is used. + If no headers match the default exponential BackOff is used instead. + items: + properties: + format: + description: The format of the reset header. + enum: + - Seconds + - UnixTimestamp + type: string + name: + description: The Name of the reset header. + maxLength: 256 + minLength: 1 + pattern: ^[a-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + required: + - format + - name + type: object + type: array + type: object + retryOn: + description: RetryOn is a list of conditions which will + cause a retry. + example: + - Canceled + - DeadlineExceeded + - Internal + - ResourceExhausted + - Unavailable + items: + enum: + - Canceled + - DeadlineExceeded + - Internal + - ResourceExhausted + - Unavailable + type: string + type: array + type: object + http: + description: HTTP defines a configuration of retries for + HTTP traffic + properties: + backOff: + description: |- + BackOff is a configuration of durations which will be used in exponential + backoff strategy between retries. + properties: + baseInterval: + default: 25ms + description: |- + BaseInterval is an amount of time which should be taken between retries. + Must be greater than zero. Values less than 1 ms are rounded up to 1 ms. + type: string + maxInterval: + description: |- + MaxInterval is a maximal amount of time which will be taken between retries. + Default is 10 times the "BaseInterval". + type: string + type: object + hostSelection: + description: |- + HostSelection is a list of predicates that dictate how hosts should be selected + when requests are retried. + items: + properties: + predicate: + description: Type is requested predicate mode. + enum: + - OmitPreviousHosts + - OmitHostsWithTags + - OmitPreviousPriorities + type: string + tags: + additionalProperties: + type: string + description: |- + Tags is a map of metadata to match against for selecting the omitted hosts. Required if Type is + OmitHostsWithTags + type: object + updateFrequency: + default: 2 + description: |- + UpdateFrequency is how often the priority load should be updated based on previously attempted priorities. + Used for OmitPreviousPriorities. + format: int32 + type: integer + required: + - predicate + type: object + type: array + hostSelectionMaxAttempts: + description: |- + HostSelectionMaxAttempts is the maximum number of times host selection will be + reattempted before giving up, at which point the host that was last selected will + be routed to. If unspecified, this will default to retrying once. + format: int64 + type: integer + numRetries: + description: |- + NumRetries is the number of attempts that will be made on failed (and + retriable) requests. If not set, the default value is 1. + format: int32 + type: integer + perTryTimeout: + description: |- + PerTryTimeout is the amount of time after which retry attempt should time out. + If left unspecified, the global route timeout for the request will be used. + Consequently, when using a 5xx based retry policy, a request that times out + will not be retried as the total timeout budget would have been exhausted. + Setting this timeout to 0 will disable it. + type: string + rateLimitedBackOff: + description: |- + RateLimitedBackOff is a configuration of backoff which will be used + when the upstream returns one of the headers configured. + properties: + maxInterval: + default: 300s + description: MaxInterval is a maximal amount of + time which will be taken between retries. + type: string + resetHeaders: + description: |- + ResetHeaders specifies the list of headers (like Retry-After or X-RateLimit-Reset) + to match against the response. Headers are tried in order, and matched + case-insensitive. The first header to be parsed successfully is used. + If no headers match the default exponential BackOff is used instead. + items: + properties: + format: + description: The format of the reset header. + enum: + - Seconds + - UnixTimestamp + type: string + name: + description: The Name of the reset header. + maxLength: 256 + minLength: 1 + pattern: ^[a-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + required: + - format + - name + type: object + type: array + type: object + retriableRequestHeaders: + description: |- + RetriableRequestHeaders is an HTTP headers which must be present in the request + for retries to be attempted. + items: + description: |- + HeaderMatch describes how to select an HTTP route by matching HTTP request + headers. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name MUST be lower case + as they will be handled with case insensitivity (See https://tools.ietf.org/html/rfc7230#section-3.2). + maxLength: 256 + minLength: 1 + pattern: ^[a-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: Type specifies how to match against + the value of the header. + enum: + - Exact + - Present + - RegularExpression + - Absent + - Prefix + type: string + value: + description: Value is the value of HTTP Header + to be matched. + type: string + required: + - name + type: object + type: array + retriableResponseHeaders: + description: |- + RetriableResponseHeaders is an HTTP response headers that trigger a retry + if present in the response. A retry will be triggered if any of the header + matches the upstream response headers. + items: + description: |- + HeaderMatch describes how to select an HTTP route by matching HTTP request + headers. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name MUST be lower case + as they will be handled with case insensitivity (See https://tools.ietf.org/html/rfc7230#section-3.2). + maxLength: 256 + minLength: 1 + pattern: ^[a-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: Type specifies how to match against + the value of the header. + enum: + - Exact + - Present + - RegularExpression + - Absent + - Prefix + type: string + value: + description: Value is the value of HTTP Header + to be matched. + type: string + required: + - name + type: object + type: array + retryOn: + description: |- + RetryOn is a list of conditions which will cause a retry. Available values are: + [5XX, GatewayError, Reset, Retriable4xx, ConnectFailure, EnvoyRatelimited, + RefusedStream, Http3PostConnectFailure, HttpMethodConnect, HttpMethodDelete, + HttpMethodGet, HttpMethodHead, HttpMethodOptions, HttpMethodPatch, + HttpMethodPost, HttpMethodPut, HttpMethodTrace]. + Also, any HTTP status code (500, 503, etc.). + example: + - 5XX + - GatewayError + - Reset + - Retriable4xx + - ConnectFailure + - EnvoyRatelimited + - RefusedStream + - Http3PostConnectFailure + - HttpMethodConnect + - HttpMethodDelete + - HttpMethodGet + - HttpMethodHead + - HttpMethodOptions + - HttpMethodPatch + - HttpMethodPost + - HttpMethodPut + - HttpMethodTrace + - "500" + - "503" + items: + type: string + type: array + type: object + tcp: + description: TCP defines a configuration of retries for + TCP traffic + properties: + maxConnectAttempt: + description: |- + MaxConnectAttempt is a maximal amount of TCP connection attempts + which will be made before giving up + format: int32 + type: integer + type: object + type: object + targetRef: + description: |- + TargetRef is a reference to the resource that represents a group of + destinations. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify + cross mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + required: + - targetRef + type: object + type: array + required: + - targetRef + type: object + type: object + served: true + storage: true + subresources: {} diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_meshservices.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshservices.yaml new file mode 100644 index 0000000000..c4548da4ef --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshservices.yaml @@ -0,0 +1,195 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: meshservices.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: MeshService + listKind: MeshServiceList + plural: meshservices + singular: meshservice + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + 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 is the specification of the Kuma MeshService resource. + properties: + identities: + items: + properties: + type: + enum: + - ServiceTag + type: string + value: + type: string + required: + - type + - value + type: object + type: array + ports: + items: + properties: + appProtocol: + default: tcp + description: Protocol identifies a protocol supported by a service. + type: string + name: + type: string + port: + format: int32 + type: integer + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + x-kubernetes-list-map-keys: + - port + - appProtocol + x-kubernetes-list-type: map + selector: + properties: + dataplaneRef: + properties: + name: + type: string + type: object + dataplaneTags: + additionalProperties: + type: string + type: object + type: object + type: object + status: + description: Status is the current status of the Kuma MeshService resource. + properties: + addresses: + items: + properties: + hostname: + type: string + hostnameGeneratorRef: + properties: + coreName: + type: string + required: + - coreName + type: object + origin: + type: string + type: object + type: array + hostnameGenerators: + items: + properties: + conditions: + description: Conditions is an array of hostname generator conditions. + items: + properties: + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + 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: + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + hostnameGeneratorRef: + properties: + coreName: + type: string + required: + - coreName + type: object + required: + - hostnameGeneratorRef + type: object + type: array + tls: + properties: + status: + enum: + - Ready + - NotReady + type: string + type: object + vips: + items: + properties: + ip: + type: string + type: object + type: array + type: object + type: object + served: true + storage: true diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_meshtcproutes.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshtcproutes.yaml new file mode 100644 index 0000000000..5ba894de81 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshtcproutes.yaml @@ -0,0 +1,281 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: meshtcproutes.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: MeshTCPRoute + listKind: MeshTCPRouteList + plural: meshtcproutes + singular: meshtcproute + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.targetRef.kind + name: TargetRef Kind + type: string + - jsonPath: .spec.targetRef.name + name: TargetRef Name + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + 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 is the specification of the Kuma MeshTCPRoute resource. + properties: + targetRef: + description: |- + TargetRef is a reference to the resource the policy takes an effect on. + The resource could be either a real store object or virtual resource + defined in-place. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify cross + mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + to: + description: |- + To list makes a match between the consumed services and corresponding + configurations + items: + properties: + rules: + description: |- + Rules contains the routing rules applies to a combination of top-level + targetRef and the targetRef in this entry. + items: + properties: + default: + description: |- + Default holds routing rules that can be merged with rules from other + policies. + properties: + backendRefs: + items: + description: BackendRef defines where to forward + traffic. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use + to identify cross mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + port: + description: Port is only supported when this + ref refers to a real MeshService object + format: int32 + type: integer + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + weight: + default: 1 + minimum: 0 + type: integer + type: object + minItems: 1 + type: array + required: + - backendRefs + type: object + required: + - default + type: object + maxItems: 1 + type: array + targetRef: + description: |- + TargetRef is a reference to the resource that represents a group of + destinations. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify + cross mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + required: + - targetRef + type: object + minItems: 1 + type: array + required: + - targetRef + type: object + type: object + served: true + storage: true + subresources: {} diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_meshtimeouts.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshtimeouts.yaml new file mode 100644 index 0000000000..f8a7205eba --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshtimeouts.yaml @@ -0,0 +1,362 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: meshtimeouts.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: MeshTimeout + listKind: MeshTimeoutList + plural: meshtimeouts + singular: meshtimeout + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.targetRef.kind + name: TargetRef Kind + type: string + - jsonPath: .spec.targetRef.name + name: TargetRef Name + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + 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 is the specification of the Kuma MeshTimeout resource. + properties: + from: + description: From list makes a match between clients and corresponding + configurations + items: + properties: + default: + description: |- + Default is a configuration specific to the group of clients referenced in + 'targetRef' + properties: + connectionTimeout: + description: |- + ConnectionTimeout specifies the amount of time proxy will wait for an TCP connection to be established. + Default value is 5 seconds. Cannot be set to 0. + type: string + http: + description: Http provides configuration for HTTP specific + timeouts + properties: + maxConnectionDuration: + description: |- + MaxConnectionDuration is the time after which a connection will be drained and/or closed, + starting from when it was first established. Setting this timeout to 0 will disable it. + Disabled by default. + type: string + maxStreamDuration: + description: |- + MaxStreamDuration is the maximum time that a stream’s lifetime will span. + Setting this timeout to 0 will disable it. Disabled by default. + type: string + requestHeadersTimeout: + description: |- + RequestHeadersTimeout The amount of time that proxy will wait for the request headers to be received. The timer is + activated when the first byte of the headers is received, and is disarmed when the last byte of + the headers has been received. If not specified or set to 0, this timeout is disabled. + Disabled by default. + type: string + requestTimeout: + description: |- + RequestTimeout The amount of time that proxy will wait for the entire request to be received. + The timer is activated when the request is initiated, and is disarmed when the last byte of the request is sent, + OR when the response is initiated. Setting this timeout to 0 will disable it. + Default is 15s. + type: string + streamIdleTimeout: + description: |- + StreamIdleTimeout is the amount of time that proxy will allow a stream to exist with no activity. + Setting this timeout to 0 will disable it. Default is 30m + type: string + type: object + idleTimeout: + description: |- + IdleTimeout is defined as the period in which there are no bytes sent or received on connection + Setting this timeout to 0 will disable it. Be cautious when disabling it because + it can lead to connection leaking. Default value is 1h. + type: string + type: object + targetRef: + description: |- + TargetRef is a reference to the resource that represents a group of + clients. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify + cross mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + required: + - targetRef + type: object + type: array + targetRef: + description: |- + TargetRef is a reference to the resource the policy takes an effect on. + The resource could be either a real store object or virtual resource + defined inplace. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify cross + mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + to: + description: To list makes a match between the consumed services and + corresponding configurations + items: + properties: + default: + description: |- + Default is a configuration specific to the group of destinations referenced in + 'targetRef' + properties: + connectionTimeout: + description: |- + ConnectionTimeout specifies the amount of time proxy will wait for an TCP connection to be established. + Default value is 5 seconds. Cannot be set to 0. + type: string + http: + description: Http provides configuration for HTTP specific + timeouts + properties: + maxConnectionDuration: + description: |- + MaxConnectionDuration is the time after which a connection will be drained and/or closed, + starting from when it was first established. Setting this timeout to 0 will disable it. + Disabled by default. + type: string + maxStreamDuration: + description: |- + MaxStreamDuration is the maximum time that a stream’s lifetime will span. + Setting this timeout to 0 will disable it. Disabled by default. + type: string + requestHeadersTimeout: + description: |- + RequestHeadersTimeout The amount of time that proxy will wait for the request headers to be received. The timer is + activated when the first byte of the headers is received, and is disarmed when the last byte of + the headers has been received. If not specified or set to 0, this timeout is disabled. + Disabled by default. + type: string + requestTimeout: + description: |- + RequestTimeout The amount of time that proxy will wait for the entire request to be received. + The timer is activated when the request is initiated, and is disarmed when the last byte of the request is sent, + OR when the response is initiated. Setting this timeout to 0 will disable it. + Default is 15s. + type: string + streamIdleTimeout: + description: |- + StreamIdleTimeout is the amount of time that proxy will allow a stream to exist with no activity. + Setting this timeout to 0 will disable it. Default is 30m + type: string + type: object + idleTimeout: + description: |- + IdleTimeout is defined as the period in which there are no bytes sent or received on connection + Setting this timeout to 0 will disable it. Be cautious when disabling it because + it can lead to connection leaking. Default value is 1h. + type: string + type: object + targetRef: + description: |- + TargetRef is a reference to the resource that represents a group of + destinations. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify + cross mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + required: + - targetRef + type: object + type: array + required: + - targetRef + type: object + type: object + served: true + storage: true + subresources: {} diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_meshtraces.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshtraces.yaml new file mode 100644 index 0000000000..2107140d32 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshtraces.yaml @@ -0,0 +1,284 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: meshtraces.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: MeshTrace + listKind: MeshTraceList + plural: meshtraces + singular: meshtrace + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.targetRef.kind + name: TargetRef Kind + type: string + - jsonPath: .spec.targetRef.name + name: TargetRef Name + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + 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 is the specification of the Kuma MeshTrace resource. + properties: + default: + description: MeshTrace configuration. + properties: + backends: + description: |- + A one element array of backend definition. + Envoy allows configuring only 1 backend, so the natural way of + representing that would be just one object. Unfortunately due to the + reasons explained in MADR 009-tracing-policy this has to be a one element + array for now. + items: + description: Only one of zipkin, datadog or openTelemetry can + be used. + properties: + datadog: + description: Datadog backend configuration. + properties: + splitService: + default: false + description: |- + Determines if datadog service name should be split based on traffic + direction and destination. For example, with `splitService: true` and a + `backend` service that communicates with a couple of databases, you would + get service names like `backend_INBOUND`, `backend_OUTBOUND_db1`, and + `backend_OUTBOUND_db2` in Datadog. + type: boolean + url: + description: |- + Address of Datadog collector, only host and port are allowed (no paths, + fragments etc.) + type: string + required: + - url + type: object + openTelemetry: + description: OpenTelemetry backend configuration. + properties: + endpoint: + description: Address of OpenTelemetry collector. + example: otel-collector:4317 + minLength: 1 + type: string + required: + - endpoint + type: object + type: + enum: + - Zipkin + - Datadog + - OpenTelemetry + type: string + zipkin: + description: Zipkin backend configuration. + properties: + apiVersion: + default: httpJson + description: |- + Version of the API. + https://github.com/envoyproxy/envoy/blob/v1.22.0/api/envoy/config/trace/v3/zipkin.proto#L66 + enum: + - httpJson + - httpProto + type: string + sharedSpanContext: + default: true + description: |- + Determines whether client and server spans will share the same span + context. + https://github.com/envoyproxy/envoy/blob/v1.22.0/api/envoy/config/trace/v3/zipkin.proto#L63 + type: boolean + traceId128bit: + default: false + description: Generate 128bit traces. + type: boolean + url: + description: Address of Zipkin collector. + type: string + required: + - url + type: object + required: + - type + type: object + maxItems: 1 + type: array + sampling: + description: |- + Sampling configuration. + Sampling is the process by which a decision is made on whether to + process/export a span or not. + properties: + client: + anyOf: + - type: integer + - type: string + default: 100% + description: |- + Target percentage of requests that will be force traced if the + 'x-client-trace-id' header is set. Mirror of client_sampling in Envoy + https://github.com/envoyproxy/envoy/blob/v1.22.0/api/envoy/config/filter/network/http_connection_manager/v2/http_connection_manager.proto#L127-L133 + Either int or decimal represented as string. + x-kubernetes-int-or-string: true + overall: + anyOf: + - type: integer + - type: string + default: 100% + description: |- + Target percentage of requests will be traced + after all other sampling checks have been applied (client, force tracing, + random sampling). This field functions as an upper limit on the total + configured sampling rate. For instance, setting client_sampling to 100% + but overall_sampling to 1% will result in only 1% of client requests with + the appropriate headers to be force traced. Mirror of + overall_sampling in Envoy + https://github.com/envoyproxy/envoy/blob/v1.22.0/api/envoy/config/filter/network/http_connection_manager/v2/http_connection_manager.proto#L142-L150 + Either int or decimal represented as string. + x-kubernetes-int-or-string: true + random: + anyOf: + - type: integer + - type: string + default: 100% + description: |- + Target percentage of requests that will be randomly selected for trace + generation, if not requested by the client or not forced. + Mirror of random_sampling in Envoy + https://github.com/envoyproxy/envoy/blob/v1.22.0/api/envoy/config/filter/network/http_connection_manager/v2/http_connection_manager.proto#L135-L140 + Either int or decimal represented as string. + x-kubernetes-int-or-string: true + type: object + tags: + description: |- + Custom tags configuration. You can add custom tags to traces based on + headers or literal values. + items: + description: |- + Custom tags configuration. + Only one of literal or header can be used. + properties: + header: + description: Tag taken from a header. + properties: + default: + description: |- + Default value to use if header is missing. + If the default is missing and there is no value the tag will not be + included. + type: string + name: + description: Name of the header. + type: string + required: + - name + type: object + literal: + description: Tag taken from literal value. + type: string + name: + description: Name of the tag. + type: string + required: + - name + type: object + type: array + type: object + targetRef: + description: |- + TargetRef is a reference to the resource the policy takes an effect on. + The resource could be either a real store object or virtual resource + defined inplace. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify cross + mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + required: + - targetRef + type: object + type: object + served: true + storage: true + subresources: {} diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_meshtrafficpermissions.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshtrafficpermissions.yaml new file mode 100644 index 0000000000..05d4337886 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_meshtrafficpermissions.yaml @@ -0,0 +1,203 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: meshtrafficpermissions.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: MeshTrafficPermission + listKind: MeshTrafficPermissionList + plural: meshtrafficpermissions + singular: meshtrafficpermission + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.targetRef.kind + name: TargetRef Kind + type: string + - jsonPath: .spec.targetRef.name + name: TargetRef Name + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + 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 is the specification of the Kuma MeshTrafficPermission + resource. + properties: + from: + description: From list makes a match between clients and corresponding + configurations + items: + properties: + default: + description: |- + Default is a configuration specific to the group of clients referenced in + 'targetRef' + properties: + action: + description: 'Action defines a behavior for the specified + group of clients:' + enum: + - Allow + - Deny + - AllowWithShadowDeny + type: string + type: object + targetRef: + description: |- + TargetRef is a reference to the resource that represents a group of + clients. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify + cross mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + required: + - targetRef + type: object + type: array + targetRef: + description: |- + TargetRef is a reference to the resource the policy takes an effect on. + The resource could be either a real store object or virtual resource + defined inplace. + properties: + kind: + description: Kind of the referenced resource + enum: + - Mesh + - MeshSubset + - MeshGateway + - MeshService + - MeshExternalService + - MeshServiceSubset + - MeshHTTPRoute + type: string + labels: + additionalProperties: + type: string + description: |- + Labels are used to select group of MeshServices that match labels. Either Labels or + Name and Namespace can be used. + type: object + mesh: + description: Mesh is reserved for future use to identify cross + mesh resources. + type: string + name: + description: |- + Name of the referenced resource. Can only be used with kinds: `MeshService`, + `MeshServiceSubset` and `MeshGatewayRoute` + type: string + namespace: + description: |- + Namespace specifies the namespace of target resource. If empty only resources in policy namespace + will be targeted. + type: string + proxyTypes: + description: |- + ProxyTypes specifies the data plane types that are subject to the policy. When not specified, + all data plane types are targeted by the policy. + items: + enum: + - Sidecar + - Gateway + type: string + minItems: 1 + type: array + sectionName: + description: |- + SectionName is used to target specific section of resource. + For example, you can target port from MeshService.ports[] by its name. Only traffic to this port will be affected. + type: string + tags: + additionalProperties: + type: string + description: |- + Tags used to select a subset of proxies by tags. Can only be used with kinds + `MeshSubset` and `MeshServiceSubset` + type: object + type: object + required: + - targetRef + type: object + type: object + served: true + storage: true + subresources: {} diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_proxytemplates.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_proxytemplates.yaml new file mode 100644 index 0000000000..7d598fb0c7 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_proxytemplates.yaml @@ -0,0 +1,50 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: proxytemplates.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: ProxyTemplate + listKind: ProxyTemplateList + plural: proxytemplates + singular: proxytemplate + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + 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 + mesh: + description: |- + Mesh is the name of the Kuma mesh this resource belongs to. + It may be omitted for cluster-scoped resources. + type: string + metadata: + type: object + spec: + description: Spec is the specification of the Kuma ProxyTemplate resource. + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: true diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_ratelimits.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_ratelimits.yaml new file mode 100644 index 0000000000..4582808832 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_ratelimits.yaml @@ -0,0 +1,50 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: ratelimits.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: RateLimit + listKind: RateLimitList + plural: ratelimits + singular: ratelimit + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + 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 + mesh: + description: |- + Mesh is the name of the Kuma mesh this resource belongs to. + It may be omitted for cluster-scoped resources. + type: string + metadata: + type: object + spec: + description: Spec is the specification of the Kuma RateLimit resource. + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: true diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_retries.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_retries.yaml new file mode 100644 index 0000000000..040efe0584 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_retries.yaml @@ -0,0 +1,50 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: retries.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: Retry + listKind: RetryList + plural: retries + singular: retry + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + 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 + mesh: + description: |- + Mesh is the name of the Kuma mesh this resource belongs to. + It may be omitted for cluster-scoped resources. + type: string + metadata: + type: object + spec: + description: Spec is the specification of the Kuma Retry resource. + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: true diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_serviceinsights.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_serviceinsights.yaml new file mode 100644 index 0000000000..69a4f709b4 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_serviceinsights.yaml @@ -0,0 +1,50 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: serviceinsights.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: ServiceInsight + listKind: ServiceInsightList + plural: serviceinsights + singular: serviceinsight + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + 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 + mesh: + description: |- + Mesh is the name of the Kuma mesh this resource belongs to. + It may be omitted for cluster-scoped resources. + type: string + metadata: + type: object + spec: + description: Spec is the specification of the Kuma ServiceInsight resource. + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: true diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_timeouts.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_timeouts.yaml new file mode 100644 index 0000000000..6599989908 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_timeouts.yaml @@ -0,0 +1,50 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: timeouts.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: Timeout + listKind: TimeoutList + plural: timeouts + singular: timeout + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + 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 + mesh: + description: |- + Mesh is the name of the Kuma mesh this resource belongs to. + It may be omitted for cluster-scoped resources. + type: string + metadata: + type: object + spec: + description: Spec is the specification of the Kuma Timeout resource. + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: true diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_trafficlogs.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_trafficlogs.yaml new file mode 100644 index 0000000000..e299ef2991 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_trafficlogs.yaml @@ -0,0 +1,50 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: trafficlogs.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: TrafficLog + listKind: TrafficLogList + plural: trafficlogs + singular: trafficlog + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + 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 + mesh: + description: |- + Mesh is the name of the Kuma mesh this resource belongs to. + It may be omitted for cluster-scoped resources. + type: string + metadata: + type: object + spec: + description: Spec is the specification of the Kuma TrafficLog resource. + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: true diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_trafficpermissions.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_trafficpermissions.yaml new file mode 100644 index 0000000000..087eecec1b --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_trafficpermissions.yaml @@ -0,0 +1,50 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: trafficpermissions.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: TrafficPermission + listKind: TrafficPermissionList + plural: trafficpermissions + singular: trafficpermission + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + 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 + mesh: + description: |- + Mesh is the name of the Kuma mesh this resource belongs to. + It may be omitted for cluster-scoped resources. + type: string + metadata: + type: object + spec: + description: Spec is the specification of the Kuma TrafficPermission resource. + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: true diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_trafficroutes.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_trafficroutes.yaml new file mode 100644 index 0000000000..6fdb809cfa --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_trafficroutes.yaml @@ -0,0 +1,50 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: trafficroutes.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: TrafficRoute + listKind: TrafficRouteList + plural: trafficroutes + singular: trafficroute + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + 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 + mesh: + description: |- + Mesh is the name of the Kuma mesh this resource belongs to. + It may be omitted for cluster-scoped resources. + type: string + metadata: + type: object + spec: + description: Spec is the specification of the Kuma TrafficRoute resource. + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: true diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_traffictraces.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_traffictraces.yaml new file mode 100644 index 0000000000..7f9832df7a --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_traffictraces.yaml @@ -0,0 +1,50 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: traffictraces.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: TrafficTrace + listKind: TrafficTraceList + plural: traffictraces + singular: traffictrace + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + 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 + mesh: + description: |- + Mesh is the name of the Kuma mesh this resource belongs to. + It may be omitted for cluster-scoped resources. + type: string + metadata: + type: object + spec: + description: Spec is the specification of the Kuma TrafficTrace resource. + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: true diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_virtualoutbounds.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_virtualoutbounds.yaml new file mode 100644 index 0000000000..c158f29bd1 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_virtualoutbounds.yaml @@ -0,0 +1,50 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: virtualoutbounds.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: VirtualOutbound + listKind: VirtualOutboundList + plural: virtualoutbounds + singular: virtualoutbound + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + 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 + mesh: + description: |- + Mesh is the name of the Kuma mesh this resource belongs to. + It may be omitted for cluster-scoped resources. + type: string + metadata: + type: object + spec: + description: Spec is the specification of the Kuma VirtualOutbound resource. + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: true diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_zoneegresses.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_zoneegresses.yaml new file mode 100644 index 0000000000..2dbcea457f --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_zoneegresses.yaml @@ -0,0 +1,56 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: zoneegresses.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: ZoneEgress + listKind: ZoneEgressList + plural: zoneegresses + singular: zoneegress + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Zone name + jsonPath: .spec.zone + name: zone + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + 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 + mesh: + description: |- + Mesh is the name of the Kuma mesh this resource belongs to. + It may be omitted for cluster-scoped resources. + type: string + metadata: + type: object + spec: + description: Spec is the specification of the Kuma ZoneEgress resource. + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: true + subresources: {} diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_zoneegressinsights.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_zoneegressinsights.yaml new file mode 100644 index 0000000000..58a9956978 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_zoneegressinsights.yaml @@ -0,0 +1,50 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: zoneegressinsights.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: ZoneEgressInsight + listKind: ZoneEgressInsightList + plural: zoneegressinsights + singular: zoneegressinsight + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + 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 + mesh: + description: |- + Mesh is the name of the Kuma mesh this resource belongs to. + It may be omitted for cluster-scoped resources. + type: string + metadata: + type: object + spec: + description: Spec is the specification of the Kuma ZoneEgressInsight resource. + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: true diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_zoneingresses.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_zoneingresses.yaml new file mode 100644 index 0000000000..8f3e835758 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_zoneingresses.yaml @@ -0,0 +1,56 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: zoneingresses.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: ZoneIngress + listKind: ZoneIngressList + plural: zoneingresses + singular: zoneingress + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Zone name + jsonPath: .spec.zone + name: zone + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + 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 + mesh: + description: |- + Mesh is the name of the Kuma mesh this resource belongs to. + It may be omitted for cluster-scoped resources. + type: string + metadata: + type: object + spec: + description: Spec is the specification of the Kuma ZoneIngress resource. + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: true + subresources: {} diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_zoneingressinsights.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_zoneingressinsights.yaml new file mode 100644 index 0000000000..66a51ae5fa --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_zoneingressinsights.yaml @@ -0,0 +1,51 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: zoneingressinsights.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: ZoneIngressInsight + listKind: ZoneIngressInsightList + plural: zoneingressinsights + singular: zoneingressinsight + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + 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 + mesh: + description: |- + Mesh is the name of the Kuma mesh this resource belongs to. + It may be omitted for cluster-scoped resources. + type: string + metadata: + type: object + spec: + description: Spec is the specification of the Kuma ZoneIngressInsight + resource. + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: true diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_zoneinsights.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_zoneinsights.yaml new file mode 100644 index 0000000000..28e26eaf7b --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_zoneinsights.yaml @@ -0,0 +1,50 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: zoneinsights.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: ZoneInsight + listKind: ZoneInsightList + plural: zoneinsights + singular: zoneinsight + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + 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 + mesh: + description: |- + Mesh is the name of the Kuma mesh this resource belongs to. + It may be omitted for cluster-scoped resources. + type: string + metadata: + type: object + spec: + description: Spec is the specification of the Kuma ZoneInsight resource. + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: true diff --git a/charts/kuma/kuma/2.8.3/crds/kuma.io_zones.yaml b/charts/kuma/kuma/2.8.3/crds/kuma.io_zones.yaml new file mode 100644 index 0000000000..e750c6388a --- /dev/null +++ b/charts/kuma/kuma/2.8.3/crds/kuma.io_zones.yaml @@ -0,0 +1,50 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: zones.kuma.io +spec: + group: kuma.io + names: + categories: + - kuma + kind: Zone + listKind: ZoneList + plural: zones + singular: zone + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + 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 + mesh: + description: |- + Mesh is the name of the Kuma mesh this resource belongs to. + It may be omitted for cluster-scoped resources. + type: string + metadata: + type: object + spec: + description: Spec is the specification of the Kuma Zone resource. + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: true diff --git a/charts/kuma/kuma/2.8.3/templates/NOTES.txt b/charts/kuma/kuma/2.8.3/templates/NOTES.txt new file mode 100644 index 0000000000..228ac26e78 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/NOTES.txt @@ -0,0 +1,42 @@ +{{ .Chart.Name }} has been installed! + +Your release is named '{{ .Release.Name }}'. + +You can access the control-plane via either the GUI, kubectl, the HTTP API, or the kumactl CLI. +{{- if .Values.noHelmHooks }} + +------------------------------------------------------------------------------- + + WARNING + + When the "noHelmHooks" value is provided, you will need to manually delete + the "ValidatingWebhookConfiguration" responsible for validating {{ include "kuma.name" . }} resources + before you can uninstall Helm release. This is because the validation provided + by the webhook is not necessary during the release removal and might potentially + even prevent you from doing it. You can do this by running the following command: + + kubectl delete ValidatingWebhookConfiguration {{ include "kuma.name" . }}-validating-webhook-configuration + + WARNING + + When the "noHelmHooks" value is set, Helm will not automatically update + the CustomResourceDefinitions (CRDs) when upgrading release. You must manually + update the CRDs if the new {{ include "kuma.name" . }} version has changes + to the CRDs. You can achieve this by calling the following command: + + kumactl install crds --no-config | kubectl apply -f + +{{- if and .Values.experimental.ebpf.enabled (not .Values.cni.enabled) }} + + WARNING + + When the "noHelmHooks" value is set, Helm will not automatically uninstall + the eBPF resources. You will need to manually uninstall these resources after + uninstalling Helm release. To do this, run the following command: + + kumactl uninstall ebpf --cleanup-image-registry {{ .Values.global.image.registry }} --cleanup-image-repository {{ .Values.dataPlane.initImage.repository }} + +{{- end }} + +------------------------------------------------------------------------------- +{{- end }} diff --git a/charts/kuma/kuma/2.8.3/templates/_helpers.tpl b/charts/kuma/kuma/2.8.3/templates/_helpers.tpl new file mode 100644 index 0000000000..0627d36fa0 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/_helpers.tpl @@ -0,0 +1,402 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "kuma.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +This is the Kuma version the chart is intended to be used with. +*/}} +{{- define "kuma.appVersion" -}} +{{- .Chart.AppVersion -}} +{{- end }} + +{{/* +This is only used in the `kuma.formatImage` function below. +*/}} +{{- define "kuma.defaultRegistry" -}} +docker.io/kumahq +{{- end }} + +{{- define "kuma.product" -}} +Kuma +{{- end }} + +{{- define "kuma.tagPrefix" -}} +{{- 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 "kuma.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 "kuma.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{- define "kuma.controlPlane.serviceName" -}} +{{- $defaultSvcName := printf "%s-control-plane" (include "kuma.name" .) -}} +{{ printf "%s" (default $defaultSvcName .Values.controlPlane.service.name) }} +{{- end }} + +{{- define "kuma.controlPlane.globalZoneSync.serviceName" -}} +{{- $defaultSvcName := printf "%s-global-zone-sync" (include "kuma.name" .) -}} +{{ printf "%s" (default $defaultSvcName .Values.controlPlane.globalZoneSyncService.name) }} +{{- end }} + +{{- define "kuma.ingress.serviceName" -}} +{{- $defaultSvcName := printf "%s-ingress" (include "kuma.name" .) -}} +{{ printf "%s" (default $defaultSvcName .Values.ingress.service.name) }} +{{- end }} + +{{- define "kuma.egress.serviceName" -}} +{{- $defaultSvcName := printf "%s-egress" (include "kuma.name" .) -}} +{{ printf "%s" (default $defaultSvcName .Values.egress.service.name) }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "kuma.labels" -}} +helm.sh/chart: {{ include "kuma.chart" . }} +{{ include "kuma.selectorLabels" . }} +{{- if (include "kuma.appVersion" .) }} +app.kubernetes.io/version: {{ (include "kuma.appVersion" .) | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "kuma.selectorLabels" -}} +app.kubernetes.io/name: {{ include "kuma.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +CNI labels +*/}} +{{- define "kuma.cniLabels" -}} +app: {{ include "kuma.name" . }}-cni +{{ include "kuma.labels" . }} +{{- end }} + +{{/* +control plane labels +*/}} +{{- define "kuma.cpLabels" -}} +app: {{ include "kuma.name" . }}-control-plane +{{- range $key, $value := $.Values.controlPlane.extraLabels }} +{{ $key | quote }}: {{ $value | quote }} +{{- end }} +{{ include "kuma.labels" . }} +{{- end }} + +{{/* +control plane deployment annotations +*/}} +{{- define "kuma.cpDeploymentAnnotations" -}} +{{- range $key, $value := $.Values.controlPlane.deploymentAnnotations }} +{{ $key | quote }}: {{ $value | quote }} +{{- end }} +{{- end }} + +{{/* +ingress labels +*/}} +{{- define "kuma.ingressLabels" -}} +app: {{ include "kuma.name" . }}-ingress +{{- range $key, $value := .Values.ingress.extraLabels }} +{{ $key | quote }}: {{ $value | quote }} +{{- end }} +{{ include "kuma.labels" . }} +{{- end }} + +{{/* +egress labels +*/}} +{{- define "kuma.egressLabels" -}} +app: {{ include "kuma.name" . }}-egress +{{ range $key, $value := .Values.egress.extraLabels }} +{{ $key | quote }}: {{ $value | quote }} +{{ end }} +{{- include "kuma.labels" . }} +{{- end }} + +{{/* +CNI selector labels +*/}} +{{- define "kuma.cniSelectorLabels" -}} +app: {{ include "kuma.name" . }}-cni +{{ include "kuma.selectorLabels" . }} +{{- end }} + +{{/* +params: { image: { registry?, repository, tag? }, root: $ } +returns: formatted image string +*/}} +{{- define "kuma.formatImage" -}} +{{- $img := .image }} +{{- $root := .root }} +{{- $registry := ($img.registry | default $root.Values.global.image.registry) -}} +{{- $repo := ($img.repository | required "Must specify image repository") -}} +{{- $product := (include "kuma.product" .) }} +{{- $tagPrefix := (include "kuma.tagPrefix" .) }} +{{- $expectedVersion := (include "kuma.appVersion" $root) }} +{{- if + and + $root.Values.global.image.tag + (ne $root.Values.global.image.tag (include "kuma.appVersion" $root)) + (eq $root.Values.global.image.registry (include "kuma.defaultRegistry" .)) +-}} +{{- fail ( + printf "This chart only supports %s version %q but %sglobal.image.tag is set to %q. Set %sglobal.image.tag to %q or skip this check by setting %s*.image.tag for each individual component." + $product $expectedVersion $tagPrefix $root.Values.global.image.tag $tagPrefix $expectedVersion $tagPrefix +) -}} +{{- end -}} +{{- $defaultTag := ($root.Values.global.image.tag | default (include "kuma.appVersion" $root)) -}} +{{- $tag := ($img.tag | default $defaultTag) -}} +{{- printf "%s/%s:%s" $registry $repo $tag -}} +{{- end -}} + +{{- define "kuma.parentEnv" -}} +{{- end -}} + +{{- define "kuma.parentSecrets" -}} +{{- end -}} + +{{- define "kuma.pluginPoliciesEnabled" -}} +{{- $list := list -}} +{{- range $k, $v := .Values.plugins.policies -}} +{{- if $v -}} +{{- $list = append $list (printf "%s" $k) -}} +{{- end -}} +{{- end -}} +{{ join "," $list }} +{{- end -}} + +{{- define "kuma.defaultEnv" -}} +env: +{{ include "kuma.parentEnv" . }} +- name: KUMA_ENVIRONMENT + value: "kubernetes" +- name: KUMA_STORE_TYPE + value: "kubernetes" +- name: KUMA_STORE_KUBERNETES_SYSTEM_NAMESPACE + value: {{ .Release.Namespace | quote }} +- name: KUMA_RUNTIME_KUBERNETES_CONTROL_PLANE_SERVICE_NAME + value: {{ include "kuma.controlPlane.serviceName" . }} +- name: KUMA_GENERAL_TLS_CERT_FILE + value: /var/run/secrets/kuma.io/tls-cert/tls.crt +- name: KUMA_GENERAL_TLS_KEY_FILE + value: /var/run/secrets/kuma.io/tls-cert/tls.key +{{- if eq .Values.controlPlane.mode "zone" }} +- name: KUMA_MULTIZONE_ZONE_GLOBAL_ADDRESS + value: {{ .Values.controlPlane.kdsGlobalAddress }} +{{- end }} +- name: KUMA_DP_SERVER_HDS_ENABLED + value: "false" +- name: KUMA_API_SERVER_READ_ONLY + value: "true" +- name: KUMA_RUNTIME_KUBERNETES_ADMISSION_SERVER_PORT + value: {{ .Values.controlPlane.admissionServerPort | default "5443" | quote }} +- name: KUMA_RUNTIME_KUBERNETES_ADMISSION_SERVER_CERT_DIR + value: /var/run/secrets/kuma.io/tls-cert +- name: KUMA_RUNTIME_KUBERNETES_INJECTOR_CNI_ENABLED + value: {{ .Values.cni.enabled | quote }} +- name: KUMA_RUNTIME_KUBERNETES_INJECTOR_SIDECAR_CONTAINER_IMAGE + value: {{ include "kuma.formatImage" (dict "image" .Values.dataPlane.image "root" $) | quote }} +- name: KUMA_INJECTOR_INIT_CONTAINER_IMAGE + value: {{ include "kuma.formatImage" (dict "image" .Values.dataPlane.initImage "root" $) | quote }} +{{- if .Values.dataPlane.dnsLogging }} +- name: KUMA_RUNTIME_KUBERNETES_INJECTOR_BUILTIN_DNS_LOGGING + value: "true" +{{- end }} +- name: KUMA_RUNTIME_KUBERNETES_INJECTOR_CA_CERT_FILE + value: /var/run/secrets/kuma.io/tls-cert/ca.crt +- name: KUMA_DEFAULTS_SKIP_MESH_CREATION + value: {{ .Values.controlPlane.defaults.skipMeshCreation | quote }} +- name: KUMA_MODE + value: {{ .Values.controlPlane.mode | quote }} +{{- if .Values.controlPlane.zone }} +- name: KUMA_MULTIZONE_ZONE_NAME + value: {{ .Values.controlPlane.zone | quote }} +{{- end }} +{{- if .Values.controlPlane.tls.apiServer.secretName }} +- name: KUMA_API_SERVER_HTTPS_TLS_CERT_FILE + value: /var/run/secrets/kuma.io/api-server-tls-cert/tls.crt +- name: KUMA_API_SERVER_HTTPS_TLS_KEY_FILE + value: /var/run/secrets/kuma.io/api-server-tls-cert/tls.key +{{- end }} +{{- if .Values.controlPlane.tls.apiServer.clientCertsSecretName }} +- name: KUMA_API_SERVER_AUTH_CLIENT_CERTS_DIR + value: /var/run/secrets/kuma.io/api-server-client-certs/ +{{- end }} +{{- if and (eq .Values.controlPlane.mode "global") (or .Values.controlPlane.tls.kdsGlobalServer.secretName .Values.controlPlane.tls.kdsGlobalServer.create) }} +- name: KUMA_MULTIZONE_GLOBAL_KDS_TLS_CERT_FILE + value: /var/run/secrets/kuma.io/kds-server-tls-cert/tls.crt +- name: KUMA_MULTIZONE_GLOBAL_KDS_TLS_KEY_FILE + value: /var/run/secrets/kuma.io/kds-server-tls-cert/tls.key +{{- end }} +{{- if and (eq .Values.controlPlane.mode "zone") (or .Values.controlPlane.tls.kdsZoneClient.secretName .Values.controlPlane.tls.kdsZoneClient.create) }} +- name: KUMA_MULTIZONE_ZONE_KDS_ROOT_CA_FILE + value: /var/run/secrets/kuma.io/kds-client-tls-cert/ca.crt +{{- end }} +- name: KUMA_API_SERVER_AUTHN_LOCALHOST_IS_ADMIN + value: "false" +- name: KUMA_RUNTIME_KUBERNETES_ALLOWED_USERS + value: "system:serviceaccount:{{ .Release.Namespace }}:{{ include "kuma.name" . }}-control-plane" +{{- if .Values.experimental.sidecarContainers }} +- name: KUMA_EXPERIMENTAL_SIDECAR_CONTAINERS + value: "true" +{{- end }} +{{- if .Values.cni.enabled }} +- name: KUMA_RUNTIME_KUBERNETES_NODE_TAINT_CONTROLLER_ENABLED + value: "true" +- name: KUMA_RUNTIME_KUBERNETES_NODE_TAINT_CONTROLLER_CNI_APP + value: "{{ include "kuma.name" . }}-cni" +- name: KUMA_RUNTIME_KUBERNETES_NODE_TAINT_CONTROLLER_CNI_NAMESPACE + value: {{ .Values.cni.namespace }} +{{- end }} +{{- if .Values.experimental.ebpf.enabled }} +- name: KUMA_RUNTIME_KUBERNETES_INJECTOR_EBPF_ENABLED + value: "true" +- name: KUMA_RUNTIME_KUBERNETES_INJECTOR_EBPF_INSTANCE_IP_ENV_VAR_NAME + value: {{ .Values.experimental.ebpf.instanceIPEnvVarName }} +- name: KUMA_RUNTIME_KUBERNETES_INJECTOR_EBPF_BPFFS_PATH + value: {{ .Values.experimental.ebpf.bpffsPath }} +- name: KUMA_RUNTIME_KUBERNETES_INJECTOR_EBPF_CGROUP_PATH + value: {{ .Values.experimental.ebpf.cgroupPath }} +- name: KUMA_RUNTIME_KUBERNETES_INJECTOR_EBPF_TC_ATTACH_IFACE + value: {{ .Values.experimental.ebpf.tcAttachIface }} +- name: KUMA_RUNTIME_KUBERNETES_INJECTOR_EBPF_PROGRAMS_SOURCE_PATH + value: {{ .Values.experimental.ebpf.programsSourcePath }} +{{- end }} +{{- if not .Values.experimental.deltaKds }} +- name: KUMA_EXPERIMENTAL_KDS_DELTA_ENABLED + value: "false" +{{- end }} +{{- if .Values.controlPlane.tls.kdsZoneClient.skipVerify }} +- name: KUMA_MULTIZONE_ZONE_KDS_TLS_SKIP_VERIFY + value: "true" +{{- end }} +- name: KUMA_PLUGIN_POLICIES_ENABLED + value: {{ include "kuma.pluginPoliciesEnabled" . | quote }} +{{- if .Values.controlPlane.supportGatewaySecretsInAllNamespaces }} +- name: KUMA_RUNTIME_KUBERNETES_SUPPORT_GATEWAY_SECRETS_IN_ALL_NAMESPACES + value: true +{{- end }} +{{- end }} + +{{- define "kuma.controlPlane.tls.general.caSecretName" -}} +{{ .Values.controlPlane.tls.general.caSecretName | default .Values.controlPlane.tls.general.secretName | default (printf "%s-tls-cert" (include "kuma.name" .)) | quote }} +{{- end }} + +{{- define "kuma.universal.defaultEnv" -}} +{{ if eq .Values.controlPlane.mode "zone" }} + {{ if .Values.ingress.enabled }} + {{ fail "Can't have ingress.enabled when running controlPlane.mode=='universal'" }} + {{ end }} + {{ if .Values.egress.enabled }} + {{ fail "Can't have egress.enabled when running controlPlane.mode=='universal'" }} + {{ end }} +{{ end }} + +env: +- name: KUMA_PLUGIN_POLICIES_ENABLED + value: {{ include "kuma.pluginPoliciesEnabled" . | quote }} +- name: KUMA_GENERAL_WORK_DIR + value: "/tmp/kuma" +- name: KUMA_ENVIRONMENT + value: "universal" +- name: KUMA_STORE_TYPE + value: "postgres" +- name: KUMA_STORE_POSTGRES_PORT + value: "{{ .Values.postgres.port }}" +- name: KUMA_DEFAULTS_SKIP_MESH_CREATION + value: {{ .Values.controlPlane.defaults.skipMeshCreation | quote }} +{{ if and (eq .Values.controlPlane.mode "zone") .Values.controlPlane.tls.general.secretName }} +- name: KUMA_GENERAL_TLS_CERT_FILE + value: /var/run/secrets/kuma.io/tls-cert/tls.crt +- name: KUMA_GENERAL_TLS_KEY_FILE + value: /var/run/secrets/kuma.io/tls-cert/tls.key +{{ end }} +- name: KUMA_MODE + value: {{ .Values.controlPlane.mode | quote }} +{{- if eq .Values.controlPlane.mode "zone" }} +- name: KUMA_MULTIZONE_ZONE_GLOBAL_ADDRESS + value: {{ .Values.controlPlane.kdsGlobalAddress }} +{{- end }} +{{- if .Values.controlPlane.zone }} +- name: KUMA_MULTIZONE_ZONE_NAME + value: {{ .Values.controlPlane.zone | quote }} +{{- end }} +{{- if and (eq .Values.controlPlane.mode "zone") (or .Values.controlPlane.tls.kdsZoneClient.secretName .Values.controlPlane.tls.kdsZoneClient.create) }} +- name: KUMA_MULTIZONE_ZONE_KDS_ROOT_CA_FILE + value: /var/run/secrets/kuma.io/kds-client-tls-cert/ca.crt +{{- end }} +{{- if not .Values.experimental.deltaKds }} +- name: KUMA_EXPERIMENTAL_KDS_DELTA_ENABLED + value: "false" +{{- end }} +{{- if .Values.controlPlane.tls.kdsZoneClient.skipVerify }} +- name: KUMA_MULTIZONE_ZONE_KDS_TLS_SKIP_VERIFY + value: "true" +{{- end }} +{{- if .Values.controlPlane.tls.apiServer.secretName }} +- name: KUMA_API_SERVER_HTTPS_TLS_CERT_FILE + value: /var/run/secrets/kuma.io/api-server-tls-cert/tls.crt +- name: KUMA_API_SERVER_HTTPS_TLS_KEY_FILE + value: /var/run/secrets/kuma.io/api-server-tls-cert/tls.key +{{- end }} +{{- if .Values.controlPlane.tls.apiServer.clientCertsSecretName }} +- name: KUMA_API_SERVER_AUTH_CLIENT_CERTS_DIR + value: /var/run/secrets/kuma.io/api-server-client-certs/ +{{- end }} +{{- if .Values.controlPlane.tls.kdsGlobalServer.secretName }} +- name: KUMA_MULTIZONE_GLOBAL_KDS_TLS_CERT_FILE + value: /var/run/secrets/kuma.io/kds-server-tls-cert/tls.crt +- name: KUMA_MULTIZONE_GLOBAL_KDS_TLS_KEY_FILE + value: /var/run/secrets/kuma.io/kds-server-tls-cert/tls.key +{{- end }} +- name: KUMA_STORE_POSTGRES_TLS_MODE + value: {{ .Values.postgres.tls.mode }} +{{- if or (eq .Values.postgres.tls.mode "verifyCa") (eq .Values.postgres.tls.mode "verifyFull") }} +{{- if empty .Values.postgres.tls.caSecretName }} +{{ fail "if mode is 'verifyCa' or 'verifyFull' then you must provide .Values.postgres.tls.caSecretName" }} +{{- end }} +{{- if .Values.postgres.tls.secretName }} +- name: KUMA_STORE_POSTGRES_TLS_CERT_PATH + value: /var/run/secrets/kuma.io/postgres-tls-cert/tls.crt +- name: KUMA_STORE_POSTGRES_TLS_KEY_PATH + value: /var/run/secrets/kuma.io/postgres-tls-cert/tls.key +{{- end }} +{{- if .Values.postgres.tls.caSecretName }} +- name: KUMA_STORE_POSTGRES_TLS_CA_PATH + value: /var/run/secrets/kuma.io/postgres-tls-cert/ca.crt +{{- end }} +{{- if .Values.postgres.tls.disableSSLSNI }} +- name: KUMA_STORE_POSTGRES_TLS_DISABLE_SSLSNI + value: {{ .Values.postgres.tls.disableSSLSNI }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/kuma/kuma/2.8.3/templates/cni-configmap.yaml b/charts/kuma/kuma/2.8.3/templates/cni-configmap.yaml new file mode 100644 index 0000000000..8d27de9efd --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/cni-configmap.yaml @@ -0,0 +1,22 @@ +{{- if and .Values.cni.enabled (not .Values.experimental.ebpf.enabled) }} +kind: ConfigMap +apiVersion: v1 +metadata: + name: {{ include "kuma.name" . }}-cni-config + namespace: {{ .Values.cni.namespace }} + labels: {{ include "kuma.cniLabels" . | nindent 4 }} +data: + # The CNI network configuration to add to the plugin chain on each node. + cni_network_config: |- + { + "cniVersion": "0.3.1", + "name": "kuma-cni", + "type": "kuma-cni", + "log_level": "{{ .Values.cni.logLevel }}", + "kubernetes": { + "kubeconfig": "__KUBECONFIG_FILEPATH__", + "cni_bin_dir": "{{ .Values.cni.binDir }}", + "exclude_namespaces": [ "kube-system" ] + } + } + {{- end }} diff --git a/charts/kuma/kuma/2.8.3/templates/cni-daemonset.yaml b/charts/kuma/kuma/2.8.3/templates/cni-daemonset.yaml new file mode 100644 index 0000000000..b5d8db7616 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/cni-daemonset.yaml @@ -0,0 +1,152 @@ +{{- if .Values.cni.enabled }} +kind: DaemonSet +apiVersion: apps/v1 +metadata: + name: {{ include "kuma.name" . }}-cni-node + namespace: {{ .Values.cni.namespace }} + annotations: + ignore-check.kube-linter.io/run-as-non-root: "The container installs a CNI plugin" + labels: {{- include "kuma.cniLabels" . | nindent 4 }} +spec: + selector: + matchLabels: + {{- include "kuma.cniSelectorLabels" . | nindent 6 }} + updateStrategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 + template: + metadata: + labels: + {{- include "kuma.cniSelectorLabels" . | nindent 8 }} + annotations: + checksum/config: {{ include (print $.Template.BasePath "/cni-configmap.yaml") . | sha256sum }} + {{- range $key, $value := .Values.cni.podAnnotations }} + {{ $key }}: {{ $value | quote }} + {{- end }} + spec: + # This, along with the CriticalAddonsOnly toleration below, + # marks the pod as a critical add-on, ensuring it gets + # priority scheduling and that its resources are reserved + # if it ever gets evicted. + priorityClassName: system-node-critical + {{- with .Values.cni.nodeSelector }} + nodeSelector: + {{ toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.cni.tolerations }} + tolerations: + {{ toYaml . | nindent 8 }} + {{- end }} + tolerations: + # Make sure kuma-cni-node gets scheduled on all nodes. + - effect: NoSchedule + operator: Exists + # Mark the pod as a critical add-on for rescheduling. + - key: CriticalAddonsOnly + operator: Exists + - effect: NoExecute + operator: Exists + serviceAccountName: {{ include "kuma.name" . }}-cni + # Minimize downtime during a rolling upgrade or deletion; tell Kubernetes to do a "force + # deletion": https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods. + terminationGracePeriodSeconds: 5 + securityContext: + {{- toYaml .Values.cni.podSecurityContext | trim | nindent 8 }} + containers: + - name: install-cni + imagePullPolicy: {{ .Values.cni.image.imagePullPolicy }} + {{- if not .Values.experimental.ebpf.enabled }} + image: {{ include "kuma.formatImage" (dict "image" .Values.cni.image "root" $) | quote }} + readinessProbe: + initialDelaySeconds: {{ .Values.cni.delayStartupSeconds }} + exec: + command: + - cat + - /tmp/ready + command: [ "sh", "-c", "--" ] + args: [ "sleep {{.Values.cni.delayStartupSeconds}} && exec /install-cni" ] + {{- else }} + {{- with .Values.cni.experimental.imageEbpf }} + image: {{ printf "%s/%s:%s" .registry .repository .tag | quote }} + {{- end }} + args: + - /app/mbctl + - --mode=kuma + - --use-reconnect=true + - --cni-mode=true + {{- if eq .Values.cni.logLevel "debug" }} + - --debug=true + {{- end }} + lifecycle: + preStop: + exec: + command: + - make + - --keep-going + - clean + {{- end }} + securityContext: + {{- toYaml .Values.cni.containerSecurityContext | trim | nindent 12 }} + {{- if .Values.experimental.ebpf.enabled }} + privileged: true + {{- end }} + {{- if not .Values.experimental.ebpf.enabled }} + env: + # Name of the CNI config file to create. + - name: CNI_CONF_NAME + value: "{{ .Values.cni.confName }}" + # The CNI network config to install on each node. + - name: CNI_NETWORK_CONFIG + valueFrom: + configMapKeyRef: + name: {{ include "kuma.name" . }}-cni-config + key: cni_network_config + - name: CNI_NET_DIR + value: "{{ .Values.cni.netDir }}" + # If true, deploy as a chained CNI plugin, otherwise deploy as a standalone CNI + - name: CHAINED_CNI_PLUGIN + value: "{{ .Values.cni.chained }}" + - name: CNI_LOG_LEVEL + value: "{{ .Values.cni.logLevel }}" + {{- end }} + resources: + {{- toYaml .Values.cni.resources | trim | nindent 12 }} + volumeMounts: + - mountPath: /host/opt/cni/bin + name: cni-bin-dir + - mountPath: /host/etc/cni/net.d + name: cni-net-dir + {{- if .Values.experimental.ebpf.enabled }} + - mountPath: /sys/fs/cgroup + name: sys-fs-cgroup + - mountPath: /host/proc + name: host-proc + - mountPath: /host/var/run + name: host-var-run + mountPropagation: Bidirectional + {{- end }} + - name: tmp + mountPath: /tmp + volumes: + # Used to install CNI. + - name: cni-bin-dir + hostPath: + path: {{ .Values.cni.binDir }} + - name: cni-net-dir + hostPath: + path: {{ .Values.cni.netDir }} + {{- if .Values.experimental.ebpf.enabled }} + - hostPath: + path: /var/run + name: host-var-run + - hostPath: + path: /sys/fs/cgroup + name: sys-fs-cgroup + - hostPath: + path: /proc + name: host-proc + {{- end }} + - name: tmp + emptyDir: {} +{{- end }} diff --git a/charts/kuma/kuma/2.8.3/templates/cni-rbac.yaml b/charts/kuma/kuma/2.8.3/templates/cni-rbac.yaml new file mode 100644 index 0000000000..07af2b2152 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/cni-rbac.yaml @@ -0,0 +1,51 @@ +{{- if .Values.cni.enabled }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "kuma.name" . }}-cni + namespace: {{ .Values.cni.namespace }} + labels: {{ include "kuma.cniLabels" . | nindent 4 }} +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: + {{- range . }} + - name: {{ . | quote }} + {{- end }} +{{- end }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "kuma.name" . }}-cni + labels: + {{ include "kuma.cniLabels" . | nindent 4 }} +rules: + - apiGroups: [""] + resources: + - nodes + verbs: + - get + - apiGroups: [""] + resources: + - pods + verbs: + - get + {{- if .Values.experimental.ebpf.enabled }} + - list + - watch + {{- end }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "kuma.name" . }}-cni + labels: + {{ include "kuma.cniLabels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "kuma.name" . }}-cni +subjects: + - kind: ServiceAccount + name: {{ include "kuma.name" . }}-cni + namespace: {{ .Values.cni.namespace }} + {{- end }} diff --git a/charts/kuma/kuma/2.8.3/templates/cp-configmap.yaml b/charts/kuma/kuma/2.8.3/templates/cp-configmap.yaml new file mode 100644 index 0000000000..cb503033fc --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/cp-configmap.yaml @@ -0,0 +1,33 @@ +{{ $kumaCpLabels := include "kuma.cpLabels" . }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "kuma.name" . }}-control-plane-config + namespace: {{ .Release.Namespace }} + labels: {{ $kumaCpLabels | nindent 4 }} +data: + config.yaml: | + # use this file to override default configuration of `kuma-cp` + # + # see conf/kuma-cp.conf.yml for available settings + {{ if .Values.controlPlane.config }} + {{ .Values.controlPlane.config | nindent 4 }} + {{ end }} + +{{- $releaseNamespace := .Release.Namespace}} +{{- range $extraConfigMap := .Values.controlPlane.extraConfigMaps }} +{{- if $extraConfigMap.values }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ $extraConfigMap.name }} + namespace: {{ $releaseNamespace }} + labels: {{ $kumaCpLabels | nindent 4 }} +data: + {{- range $fileName, $fileContents := $extraConfigMap.values }} + {{- $fileName | nindent 2 }}: | + {{- $fileContents | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} diff --git a/charts/kuma/kuma/2.8.3/templates/cp-deployment.yaml b/charts/kuma/kuma/2.8.3/templates/cp-deployment.yaml new file mode 100644 index 0000000000..61bb2d27f6 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/cp-deployment.yaml @@ -0,0 +1,398 @@ +{{ $kdsGlobalServerTLSSecretName := "" }} +{{ if eq .Values.controlPlane.mode "global" }} + {{ $kdsGlobalServerTLSSecretName = .Values.controlPlane.tls.kdsGlobalServer.secretName }} + {{ if and .Values.controlPlane.tls.kdsGlobalServer.create (not $kdsGlobalServerTLSSecretName) }} + {{ $kdsGlobalServerTLSSecretName = print (include "kuma.name" .) "-kds-global-server-tls" }} + {{ end }} +{{ end }} + +{{ $kdsZoneClientTLSSecretName := "" }} +{{ if eq .Values.controlPlane.mode "zone" }} + {{ $kdsZoneClientTLSSecretName = .Values.controlPlane.tls.kdsZoneClient.secretName }} + {{ if and .Values.controlPlane.tls.kdsZoneClient.create (not $kdsZoneClientTLSSecretName) }} + {{ $kdsZoneClientTLSSecretName = print (include "kuma.name" .) "-kds-zone-client-tls" }} + {{ end }} +{{ end }} + +{{ if not (or (eq .Values.controlPlane.mode "zone") (eq .Values.controlPlane.mode "global") (eq .Values.controlPlane.mode "standalone")) }} + {{ $msg := printf "controlPlane.mode invalid got:'%s' supported values: global,zone,standalone" .Values.controlPlane.mode }} + {{ fail $msg }} +{{ end }} +{{ if eq .Values.controlPlane.mode "zone" }} + {{ if not (empty .Values.controlPlane.zone) }} + {{ if gt (len .Values.controlPlane.zone) 253 }} + {{ fail "controlPlane.zone must be no more than 253 characters" }} + {{ else }} + {{ if not (regexMatch "^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$" .Values.controlPlane.zone) }} + {{ fail "controlPlane.zone must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character" }} + {{ end }} + {{ end }} + {{ end }} + {{ if not (empty .Values.controlPlane.kdsGlobalAddress) }} + {{ $url := urlParse .Values.controlPlane.kdsGlobalAddress }} + {{ if not (or (eq $url.scheme "grpcs") (eq $url.scheme "grpc")) }} + {{ $msg := printf "controlPlane.kdsGlobalAddress must be a url with scheme grpcs:// or grpc:// got:'%s'" .Values.controlPlane.kdsGlobalAddress }} + {{ fail $msg }} + {{ end }} + {{ end }} +{{ else }} + {{ if not (empty .Values.controlPlane.zone) }} + {{ fail "Can't specify a controlPlane.zone when controlPlane.mode!='zone'" }} + {{ end }} + {{ if not (empty .Values.controlPlane.kdsGlobalAddress) }} + {{ fail "Can't specify a controlPlane.kdsGlobalAddress when controlPlane.mode!='zone'" }} + {{ end }} +{{ end }} + +{{- $defaultEnv := include "kuma.defaultEnv" . | fromYaml | pluck "env" | first }} +{{- if eq .Values.controlPlane.environment "universal" }} +{{- $defaultEnv = include "kuma.universal.defaultEnv" . | fromYaml | pluck "env" | first }} +{{- end }} +{{- $defaultEnvDict := dict }} +{{- range $index, $item := $defaultEnv }} +{{- $name := $item.name | upper }} +{{- $defaultEnvDict := set $defaultEnvDict $name $item.value }} +{{- end }} +{{- $envVarsCopy := deepCopy .Values.controlPlane.envVars }} +{{- $mergedEnv := merge $envVarsCopy $defaultEnvDict }} +{{- $defaultSecrets := include "kuma.parentSecrets" . | fromYaml }} +{{- $extraSecrets := .Values.controlPlane.extraSecrets }} +{{- $mergedSecrets := merge $extraSecrets $defaultSecrets }} + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "kuma.name" . }}-control-plane + namespace: {{ .Release.Namespace }} + labels: {{ include "kuma.cpLabels" . | nindent 4 }} + annotations: {{ include "kuma.cpDeploymentAnnotations" . | nindent 4 }} +spec: + {{- if not .Values.controlPlane.autoscaling.enabled }} + replicas: {{ .Values.controlPlane.replicas }} + {{- end }} + minReadySeconds: {{ .Values.controlPlane.minReadySeconds }} + strategy: + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 + selector: + matchLabels: + {{- include "kuma.selectorLabels" . | nindent 6 }} + app: {{ include "kuma.name" . }}-control-plane + template: + metadata: + annotations: + checksum/config: {{ include (print $.Template.BasePath "/cp-configmap.yaml") . | sha256sum }} + {{- if .Values.restartOnSecretChange }} + checksum/tls-secrets: {{ include (print $.Template.BasePath "/cp-webhooks-and-secrets.yaml") . | sha256sum }} + {{- end }} + {{- range $key, $value := $.Values.controlPlane.podAnnotations }} + {{ $key }}: {{ $value | quote }} + {{- end }} + labels: {{ include "kuma.cpLabels" . | nindent 8 }} + spec: + {{- with .Values.controlPlane.affinity }} + affinity: {{ tpl (toYaml . | nindent 8) $ }} + {{- end }} + {{- with .Values.controlPlane.topologySpreadConstraints }} + topologySpreadConstraints: {{ tpl (toYaml . | nindent 8) $ }} + {{- end }} + securityContext: + {{- toYaml .Values.controlPlane.podSecurityContext | trim | nindent 8 }} + serviceAccountName: {{ include "kuma.name" . }}-control-plane + automountServiceAccountToken: {{ .Values.controlPlane.automountServiceAccountToken }} + {{- with .Values.controlPlane.nodeSelector }} + nodeSelector: + {{ toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.controlPlane.tolerations }} + tolerations: + {{ toYaml . | nindent 8 }} + {{- end }} + hostNetwork: {{ .Values.controlPlane.hostNetwork }} + terminationGracePeriodSeconds: {{ .Values.controlPlane.terminationGracePeriodSeconds }} + {{- if (eq .Values.controlPlane.environment "universal") }} + initContainers: + - name: migration + image: {{ include "kuma.formatImage" (dict "image" .Values.controlPlane.image "root" $) | quote }} + imagePullPolicy: {{ .Values.controlPlane.image.pullPolicy }} + securityContext: + {{- toYaml .Values.controlPlane.containerSecurityContext | trim | nindent 12 }} + env: + {{- range $key, $value := $mergedEnv }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + {{- range $element := .Values.controlPlane.secrets }} + - name: {{ $element.Env }} + valueFrom: + secretKeyRef: + name: {{ $element.Secret }} + key: {{ $element.Key }} + {{- end }} + args: + - migrate + - up + - --log-level=info + - --config-file=/etc/kuma.io/kuma-control-plane/config.yaml + resources: + {{- if .Values.controlPlane.resources }} + {{- .Values.controlPlane.resources | toYaml | nindent 12 }} + {{- end }} + volumeMounts: + {{- if .Values.postgres.tls.caSecretName }} + - name: postgres-tls-cert-ca + subPath: ca.crt + mountPath: /var/run/secrets/kuma.io/postgres-tls-cert/ca.crt + readOnly: true + {{- end }} + {{- if .Values.postgres.tls.secretName }} + - name: postgres-tls-cert + subPath: tls.crt + mountPath: /var/run/secrets/kuma.io/postgres-tls-cert/tls.crt + readOnly: true + - name: postgres-tls-cert + subPath: tls.key + mountPath: /var/run/secrets/kuma.io/postgres-tls-cert/tls.key + readOnly: true + {{- end }} + - name: {{ include "kuma.name" . }}-control-plane-config + mountPath: /etc/kuma.io/kuma-control-plane + readOnly: true + {{- end }} + containers: + - name: control-plane + image: {{ include "kuma.formatImage" (dict "image" .Values.controlPlane.image "root" $) | quote }} + imagePullPolicy: {{ .Values.controlPlane.image.pullPolicy }} + securityContext: + {{- toYaml .Values.controlPlane.containerSecurityContext | trim | nindent 12 }} + env: + {{- range $key, $value := $mergedEnv }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + {{- range $element := .Values.controlPlane.secrets }} + - name: {{ $element.Env }} + valueFrom: + secretKeyRef: + name: {{ $element.Secret }} + key: {{ $element.Key }} + {{- end }} + - name: KUMA_INTER_CP_CATALOG_INSTANCE_ADDRESS + valueFrom: + fieldRef: + fieldPath: status.podIP + args: + - run + - --log-level={{ .Values.controlPlane.logLevel }} + - --log-output-path={{ .Values.controlPlane.logOutputPath }} + - --config-file=/etc/kuma.io/kuma-control-plane/config.yaml + ports: + - containerPort: 5680 + name: diagnostics + protocol: TCP + - containerPort: 5681 + - containerPort: 5682 + - containerPort: {{ .Values.controlPlane.admissionServerPort | default "5443" }} + {{- if ne .Values.controlPlane.mode "global" }} + - containerPort: 5678 + {{- end }} + livenessProbe: + timeoutSeconds: 10 + httpGet: + path: /healthy + port: 5680 + readinessProbe: + timeoutSeconds: 10 + httpGet: + path: /ready + port: 5680 + resources: + {{- if .Values.controlPlane.resources }} + {{- .Values.controlPlane.resources | toYaml | nindent 12 }} + {{- end }} + {{ with .Values.controlPlane.lifecycle }} + lifecycle: {{ . | toYaml | nindent 14 }} + {{ end }} + volumeMounts: + {{- if eq .Values.controlPlane.environment "kubernetes" }} + {{- if not .Values.controlPlane.automountServiceAccountToken }} + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + name: serviceaccount-token + readOnly: true + {{- end }} + - name: general-tls-cert + mountPath: /var/run/secrets/kuma.io/tls-cert/tls.crt + subPath: tls.crt + readOnly: true + - name: general-tls-cert + mountPath: /var/run/secrets/kuma.io/tls-cert/tls.key + subPath: tls.key + readOnly: true + - name: general-tls-cert{{- if .Values.controlPlane.tls.general.caSecretName }}-ca{{- end }} + mountPath: /var/run/secrets/kuma.io/tls-cert/ca.crt + subPath: ca.crt + readOnly: true + {{- end }} + {{- if and (eq .Values.controlPlane.environment "universal") (eq .Values.controlPlane.mode "zone") }} + {{- if .Values.controlPlane.tls.general.secretName }} + - name: general-tls-cert + mountPath: /var/run/secrets/kuma.io/tls-cert/tls.crt + subPath: tls.crt + readOnly: true + - name: general-tls-cert + mountPath: /var/run/secrets/kuma.io/tls-cert/tls.key + subPath: tls.key + readOnly: true + - name: general-tls-cert{{- if .Values.controlPlane.tls.general.caSecretName }}-ca{{- end }} + mountPath: /var/run/secrets/kuma.io/tls-cert/ca.crt + subPath: ca.crt + readOnly: true + {{- end }} + {{- end }} + - name: {{ include "kuma.name" . }}-control-plane-config + mountPath: /etc/kuma.io/kuma-control-plane + readOnly: true + {{- if .Values.controlPlane.tls.apiServer.secretName }} + - name: api-server-tls-cert + mountPath: /var/run/secrets/kuma.io/api-server-tls-cert + readOnly: true + {{- end }} + {{- if .Values.postgres.tls.caSecretName }} + - name: postgres-tls-cert-ca + subPath: ca.crt + mountPath: /var/run/secrets/kuma.io/postgres-tls-cert/ca.crt + readOnly: true + {{- end }} + {{- if .Values.postgres.tls.secretName }} + - name: postgres-tls-cert + subPath: tls.crt + mountPath: /var/run/secrets/kuma.io/postgres-tls-cert/tls.crt + readOnly: true + - name: postgres-tls-cert + subPath: tls.key + mountPath: /var/run/secrets/kuma.io/postgres-tls-cert/tls.key + readOnly: true + {{- end }} + {{- if .Values.controlPlane.tls.apiServer.clientCertsSecretName }} + - name: api-server-client-certs + mountPath: /var/run/secrets/kuma.io/api-server-client-certs + readOnly: true + {{- end }} + {{- if $kdsGlobalServerTLSSecretName }} + - name: kds-server-tls-cert + mountPath: /var/run/secrets/kuma.io/kds-server-tls-cert + readOnly: true + {{- end }} + {{- if $kdsZoneClientTLSSecretName }} + - name: kds-client-tls-cert + mountPath: /var/run/secrets/kuma.io/kds-client-tls-cert + readOnly: true + {{- end }} + {{- range $extraConfigMap := .Values.controlPlane.extraConfigMaps }} + - name: {{ $extraConfigMap.name }} + mountPath: {{ $extraConfigMap.mountPath }} + readOnly: {{ $extraConfigMap.readOnly }} + {{- end }} + {{- range $mergedSecret := $mergedSecrets }} + - name: {{ $mergedSecret.name }} + mountPath: {{ $mergedSecret.mountPath }} + subPath: {{ $mergedSecret.subPath }} + readOnly: {{ $mergedSecret.readOnly }} + {{- end }} + - name: tmp + mountPath: /tmp + volumes: + {{- if eq .Values.controlPlane.environment "kubernetes" }} + {{- if not .Values.controlPlane.automountServiceAccountToken }} + - name: serviceaccount-token + projected: + defaultMode: 420 + sources: + - serviceAccountToken: + expirationSeconds: 3600 + path: token + - configMap: + name: kube-root-ca.crt + items: + - key: ca.crt + path: ca.crt + - downwardAPI: + items: + - fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + path: namespace + {{- end }} + {{- if .Values.controlPlane.tls.general.secretName }} + - name: general-tls-cert + secret: + secretName: {{ .Values.controlPlane.tls.general.secretName }} + {{- else }} + - name: general-tls-cert + secret: + secretName: {{ include "kuma.name" . }}-tls-cert + {{- end }} + {{- if .Values.controlPlane.tls.general.caSecretName }} + - name: general-tls-cert-ca + secret: + secretName: {{ .Values.controlPlane.tls.general.caSecretName }} + {{- end }} + {{- end }} + {{- if and (eq .Values.controlPlane.environment "universal") (eq .Values.controlPlane.mode "zone") }} + {{- if .Values.controlPlane.tls.general.secretName }} + - name: general-tls-cert + secret: + secretName: {{ .Values.controlPlane.tls.general.secretName }} + {{- end }} + {{- if .Values.controlPlane.tls.general.caSecretName }} + - name: general-tls-cert-ca + secret: + secretName: {{ .Values.controlPlane.tls.general.caSecretName }} + {{- end }} + {{- end }} + {{- if .Values.controlPlane.tls.apiServer.secretName }} + - name: api-server-tls-cert + secret: + secretName: {{ .Values.controlPlane.tls.apiServer.secretName }} + {{- end }} + {{- if .Values.postgres.tls.caSecretName }} + - name: postgres-tls-cert-ca + secret: + secretName: {{ .Values.postgres.tls.caSecretName }} + {{- end }} + {{- if .Values.postgres.tls.secretName }} + - name: postgres-tls-cert + secret: + secretName: {{ .Values.postgres.tls.secretName }} + {{- end }} + {{- if .Values.controlPlane.tls.apiServer.clientCertsSecretName }} + - name: api-server-client-certs + secret: + secretName: {{ .Values.controlPlane.tls.apiServer.clientCertsSecretName }} + {{- end }} + {{- if $kdsGlobalServerTLSSecretName }} + - name: kds-server-tls-cert + secret: + secretName: {{ $kdsGlobalServerTLSSecretName }} + {{- end }} + {{- if $kdsZoneClientTLSSecretName }} + - name: kds-client-tls-cert + secret: + secretName: {{ $kdsZoneClientTLSSecretName }} + {{- end }} + - name: {{ include "kuma.name" . }}-control-plane-config + configMap: + name: {{ include "kuma.name" . }}-control-plane-config + {{- range $extraConfigMap := .Values.controlPlane.extraConfigMaps }} + - name: {{ $extraConfigMap.name }} + configMap: + name: {{ $extraConfigMap.name }} + {{- end }} + {{- range $mergedSecret := $mergedSecrets }} + - name: {{ $mergedSecret.name }} + secret: + secretName: {{ $mergedSecret.name }} + {{- end }} + - name: tmp + emptyDir: {} diff --git a/charts/kuma/kuma/2.8.3/templates/cp-global-sync-service.yaml b/charts/kuma/kuma/2.8.3/templates/cp-global-sync-service.yaml new file mode 100644 index 0000000000..c5b3555a8a --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/cp-global-sync-service.yaml @@ -0,0 +1,33 @@ +{{- if and (eq .Values.controlPlane.mode "global") .Values.controlPlane.globalZoneSyncService.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "kuma.controlPlane.globalZoneSync.serviceName" . }} + namespace: {{ .Release.Namespace }} + annotations: + {{- range $key, $value := .Values.controlPlane.globalZoneSyncService.annotations }} + {{ $key }}: {{ $value | quote }} + {{- end }} + labels: {{ include "kuma.cpLabels" . | nindent 4 }} +spec: + type: {{ .Values.controlPlane.globalZoneSyncService.type }} + {{- if .Values.controlPlane.globalZoneSyncService.loadBalancerIP }} + loadBalancerIP: {{ .Values.controlPlane.globalZoneSyncService.loadBalancerIP }} + {{- end }} + {{- if .Values.controlPlane.globalZoneSyncService.loadBalancerSourceRanges }} + loadBalancerSourceRanges: + {{- range .Values.controlPlane.globalZoneSyncService.loadBalancerSourceRanges }} + - {{.}} + {{- end }} + {{- end }} + ports: + - port: {{ .Values.controlPlane.globalZoneSyncService.port }} + appProtocol: {{ .Values.controlPlane.globalZoneSyncService.protocol }} + {{- if and (eq .Values.controlPlane.globalZoneSyncService.type "NodePort") .Values.controlPlane.globalZoneSyncService.nodePort }} + nodePort: {{ .Values.controlPlane.globalZoneSyncService.nodePort }} + {{- end }} + name: global-zone-sync + selector: + app: {{ include "kuma.name" . }}-control-plane + {{ include "kuma.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/charts/kuma/kuma/2.8.3/templates/cp-hpa.yaml b/charts/kuma/kuma/2.8.3/templates/cp-hpa.yaml new file mode 100644 index 0000000000..dc4981020f --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/cp-hpa.yaml @@ -0,0 +1,24 @@ +{{- if .Values.controlPlane.autoscaling.enabled }} +{{ if .Capabilities.APIVersions.Has "autoscaling/v2" }} +apiVersion: "autoscaling/v2" +{{ else }} +apiVersion: "autoscaling/v1" +{{ end }} +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "kuma.name" . }}-control-plane + namespace: {{ .Release.Namespace }} + labels: {{ include "kuma.cpLabels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "kuma.name" . }}-control-plane + minReplicas: {{ .Values.controlPlane.autoscaling.minReplicas }} + maxReplicas: {{ .Values.controlPlane.autoscaling.maxReplicas }} + {{ if .Capabilities.APIVersions.Has "autoscaling/v2" }} + metrics: {{- toYaml .Values.controlPlane.autoscaling.metrics | nindent 4 }} + {{ else }} + targetCPUUtilizationPercentage: {{ .Values.controlPlane.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/charts/kuma/kuma/2.8.3/templates/cp-ingress.yaml b/charts/kuma/kuma/2.8.3/templates/cp-ingress.yaml new file mode 100644 index 0000000000..8ceae01f8c --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/cp-ingress.yaml @@ -0,0 +1,25 @@ +{{- if .Values.controlPlane.ingress.enabled }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "kuma.controlPlane.serviceName" . }} + namespace: {{ .Release.Namespace }} + {{- with .Values.controlPlane.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + labels: {{ include "kuma.cpLabels" . | nindent 4 }} +spec: + ingressClassName: {{ .Values.controlPlane.ingress.ingressClassName }} + rules: + - host: {{ .Values.controlPlane.ingress.hostname }} + http: + paths: + - path: {{ .Values.controlPlane.ingress.path }} + pathType: {{ .Values.controlPlane.ingress.pathType }} + backend: + service: + name: {{ include "kuma.controlPlane.serviceName" . }} + port: + number: {{ .Values.controlPlane.ingress.servicePort }} +{{- end }} diff --git a/charts/kuma/kuma/2.8.3/templates/cp-kds-global-server-secret.yaml b/charts/kuma/kuma/2.8.3/templates/cp-kds-global-server-secret.yaml new file mode 100644 index 0000000000..5ea3314a3e --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/cp-kds-global-server-secret.yaml @@ -0,0 +1,15 @@ +{{ if and (eq .Values.controlPlane.mode "global") .Values.controlPlane.tls.kdsGlobalServer.create }} +apiVersion: v1 +kind: Secret +metadata: +{{ with .Values.controlPlane.tls.kdsGlobalServer.secretName }} + name: {{ . }} +{{ else }} + name: {{ include "kuma.name" . }}-kds-global-server-tls +{{ end }} + labels: {{ include "kuma.cpLabels" . | nindent 4 }} +type: kubernetes.io/tls +stringData: + tls.crt: {{ required "you must provide a kds tls cert" .Values.controlPlane.tls.kdsGlobalServer.cert | quote }} + tls.key: {{ required "you must provide a kds tls key" .Values.controlPlane.tls.kdsGlobalServer.key | quote }} +{{ end }} diff --git a/charts/kuma/kuma/2.8.3/templates/cp-kds-zone-client-tls-secret.yaml b/charts/kuma/kuma/2.8.3/templates/cp-kds-zone-client-tls-secret.yaml new file mode 100644 index 0000000000..99b15c5bdc --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/cp-kds-zone-client-tls-secret.yaml @@ -0,0 +1,13 @@ +{{ if and (eq .Values.controlPlane.mode "zone") .Values.controlPlane.tls.kdsZoneClient.create }} +apiVersion: v1 +kind: Secret +metadata: +{{ with .Values.controlPlane.tls.kdsZoneClient.secretName }} + name: {{ . }} +{{ else }} + name: {{ include "kuma.name" . }}-kds-zone-client-tls +{{ end }} + labels: {{ include "kuma.cpLabels" . | nindent 4 }} +stringData: + ca.crt: {{ required "you must provide a kds cert" .Values.controlPlane.tls.kdsZoneClient.cert | quote }} +{{ end }} diff --git a/charts/kuma/kuma/2.8.3/templates/cp-pdb.yaml b/charts/kuma/kuma/2.8.3/templates/cp-pdb.yaml new file mode 100644 index 0000000000..bb29bfd208 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/cp-pdb.yaml @@ -0,0 +1,20 @@ +{{ if $.Values.controlPlane.podDisruptionBudget.enabled }} +{{ if .Capabilities.APIVersions.Has "policy/v1" }} +apiVersion: policy/v1 +{{ else if .Capabilities.APIVersions.Has "policy/v1beta1" }} +apiVersion: policy/v1beta1 +{{ else }} +{{ fail "pod disruption budgets are not supported by this version of kubernetes" }} +{{ end }} +kind: PodDisruptionBudget +metadata: + name: {{ include "kuma.name" . }}-control-plane + namespace: {{ .Release.Namespace }} + labels: {{ include "kuma.cpLabels" . | nindent 4 }} +spec: + maxUnavailable: {{ .Values.controlPlane.podDisruptionBudget.maxUnavailable }} + selector: + matchLabels: + {{- include "kuma.selectorLabels" . | nindent 6 }} + app: {{ include "kuma.name" . }}-control-plane +{{ end }} diff --git a/charts/kuma/kuma/2.8.3/templates/cp-rbac.yaml b/charts/kuma/kuma/2.8.3/templates/cp-rbac.yaml new file mode 100644 index 0000000000..2c0145f0c1 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/cp-rbac.yaml @@ -0,0 +1,315 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "kuma.name" . }}-control-plane + namespace: {{ .Release.Namespace }} + labels: {{ include "kuma.cpLabels" . | nindent 4 }} +{{- with .Values.controlPlane.serviceAccountAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} +{{- end }} +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: + {{- range . }} + - name: {{ . | quote }} + {{- end }} +{{- end }} +{{- if (eq .Values.controlPlane.environment "kubernetes") }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "kuma.name" . }}-control-plane + labels: {{ include "kuma.cpLabels" . | nindent 4 }} +rules: + - apiGroups: + - "" + resources: + - namespaces + - pods + - configmaps + - nodes +{{- if .Values.controlPlane.supportGatewaySecretsInAllNamespaces }} + - secrets +{{- end }} + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - secrets + verbs: + - list + - watch + - apiGroups: + - "discovery.k8s.io" + resources: + - endpointslices + verbs: + - get + - list + - watch + - apiGroups: + - "apps" + resources: + - deployments + - replicasets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - "batch" + resources: + - jobs + verbs: + - get + - list + - watch + - apiGroups: + - gateway.networking.k8s.io + resources: + - gatewayclasses + - gateways + - referencegrants + - httproutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - gateway.networking.k8s.io + resources: + - gatewayclasses/status + - gateways/status + - httproutes/status + verbs: + - get + - patch + - update + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + - apiGroups: + - "" + resources: + - services + verbs: + - get + - delete + - list + - watch + - create + - update + - patch + - apiGroups: + - "discovery.k8s.io" + resources: + - endpointslices + verbs: + - get + - list + - watch + - apiGroups: + - kuma.io + resources: + - dataplanes + - dataplaneinsights + - meshes + - zones + - zoneinsights + - zoneingresses + - zoneingressinsights + - zoneegresses + - zoneegressinsights + - meshinsights + - serviceinsights + - proxytemplates + - ratelimits + - trafficpermissions + - trafficroutes + - timeouts + - retries + - circuitbreakers + - virtualoutbounds + - containerpatches + - externalservices + - faultinjections + - healthchecks + - trafficlogs + - traffictraces + - meshgateways + - meshgatewayroutes + - meshgatewayinstances + - meshgatewayconfigs + {{- range $policy, $v := .Values.plugins.policies }} + {{- if $v }} + - {{ $policy }} + {{- end}} + {{- end}} + {{- range $policy, $v := .Values.plugins.resources }} + {{- if $v }} + - {{ $policy }} + {{- end}} + {{- end}} + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - kuma.io + resources: + - meshgatewayinstances/status + - meshgatewayinstances/finalizers + - meshes/finalizers + - dataplanes/finalizers + verbs: + - get + - patch + - update + - apiGroups: + - "" + resources: + - pods/finalizers + verbs: + - get + - patch + - update + {{- if .Values.cni.enabled }} + - apiGroups: + - k8s.cni.cncf.io + resources: + - network-attachment-definitions + verbs: + - get + - list + - watch + - create + - delete + - apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - nodes + verbs: + - update + - apiGroups: + - "pods" + resources: + - pods + verbs: + - list + {{- end }} + # validate k8s token before issuing mTLS cert + - apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "kuma.name" . }}-control-plane + labels: {{ include "kuma.cpLabels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "kuma.name" . }}-control-plane +subjects: + - kind: ServiceAccount + name: {{ include "kuma.name" . }}-control-plane + namespace: {{ .Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "kuma.name" . }}-control-plane + namespace: {{ .Release.Namespace }} + labels: {{ include "kuma.cpLabels" . | nindent 4 }} +rules: + - apiGroups: + - "" + resources: + - secrets + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + # leader-for-life election deletes Pods in some circumstances + - apiGroups: + - "" + resources: + - pods + verbs: + - delete +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "kuma.name" . }}-control-plane + namespace: {{ .Release.Namespace }} + labels: {{ include "kuma.cpLabels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "kuma.name" . }}-control-plane +subjects: + - kind: ServiceAccount + name: {{ include "kuma.name" . }}-control-plane + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/kuma/kuma/2.8.3/templates/cp-service.yaml b/charts/kuma/kuma/2.8.3/templates/cp-service.yaml new file mode 100644 index 0000000000..3b9c3e31fd --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/cp-service.yaml @@ -0,0 +1,49 @@ +{{ if .Values.controlPlane.service.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "kuma.controlPlane.serviceName" . }} + namespace: {{ .Release.Namespace }} + labels: {{ include "kuma.cpLabels" . | nindent 4 }} + annotations: + {{- range $key, $value := .Values.controlPlane.service.annotations }} + {{- if $value }} + {{ $key }}: {{ $value | quote }} + {{- end }} + {{- end }} +spec: + type: {{ .Values.controlPlane.service.type }} + ports: + - port: 5680 + name: diagnostics + appProtocol: http + - port: 5681 + name: http-api-server + appProtocol: http + {{- if and (eq .Values.controlPlane.service.type "NodePort") .Values.controlPlane.service.apiServer.http.nodePort }} + nodePort: {{ .Values.controlPlane.service.apiServer.http.nodePort }} + {{- end }} + - port: 5682 + name: https-api-server + appProtocol: https + {{- if and (eq .Values.controlPlane.service.type "NodePort") .Values.controlPlane.service.apiServer.https.nodePort }} + nodePort: {{ .Values.controlPlane.service.apiServer.https.nodePort }} + {{- end }} + {{- if ne .Values.controlPlane.environment "universal" }} + - port: 443 + name: https-admission-server + targetPort: {{ .Values.controlPlane.admissionServerPort | default "5443" }} + appProtocol: https + {{- end }} + {{- if ne .Values.controlPlane.mode "global" }} + - port: 5676 + name: mads-server + appProtocol: https + - port: 5678 + name: dp-server + appProtocol: https + {{- end }} + selector: + app: {{ include "kuma.name" . }}-control-plane + {{- include "kuma.selectorLabels" . | nindent 4 }} +{{ end }} diff --git a/charts/kuma/kuma/2.8.3/templates/cp-webhooks-and-secrets.yaml b/charts/kuma/kuma/2.8.3/templates/cp-webhooks-and-secrets.yaml new file mode 100644 index 0000000000..8d9ba31693 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/cp-webhooks-and-secrets.yaml @@ -0,0 +1,337 @@ +{{- if not (eq (empty .Values.controlPlane.tls.general.caBundle) (empty .Values.controlPlane.tls.general.secretName)) }} + {{ fail "You need to send both or neither of controlPlane.tls.general.caBundle and controlPlane.tls.general.secretName"}} +{{- end }} +{{- $caBundle := .Values.controlPlane.tls.general.caBundle }} +{{/* +Generate certificates +see: https://masterminds.github.io/sprig/crypto.html +see: https://medium.com/nuvo-group-tech/move-your-certs-to-helm-4f5f61338aca +see: https://github.com/networkservicemesh/networkservicemesh/blob/804ad5026bb5dbd285c220f15395fe25e46f5edb/deployments/helm/nsm/charts/admission-webhook/templates/admission-webhook-secret.tpl + +We only autogenerate certs if user did not chose their own secret. +We only autogenerate certs if the cert is not yet generated. This way we keep the secrets between HELM upgrades. +*/}} + +{{- if eq .Values.controlPlane.tls.general.secretName "" -}} +{{- $cert := "" }} +{{- $key := "" }} +{{- $secretName := print (include "kuma.name" .) "-tls-cert" }} + +{{- $secret := (lookup "v1" "Secret" .Release.Namespace $secretName) -}} +{{- if $secret -}} + {{- $cert = index $secret.data "tls.crt" -}} + {{- $key = index $secret.data "tls.key" -}} + {{- $caBundle = index $secret.data "ca.crt" -}} +{{- else -}} + {{- $commonName := (include "kuma.controlPlane.serviceName" .) -}} + {{- $altNames := list (printf "%s.%s" $commonName .Release.Namespace) (printf "%s.%s.svc" $commonName .Release.Namespace) -}} + {{- $certTTL := 3650 -}} + {{- $ca := genCA "kuma-ca" $certTTL -}} + + {{- $genCert := genSignedCert $commonName nil $altNames $certTTL $ca -}} + {{- $cert = $genCert.Cert | b64enc -}} + {{- $key = $genCert.Key | b64enc -}} + {{ $caBundle = $ca.Cert | b64enc }} +{{- end -}} +--- +apiVersion: v1 +kind: Secret +type: kubernetes.io/tls +metadata: + name: {{ $secretName }} + namespace: {{ .Release.Namespace }} + labels: {{ include "kuma.cpLabels" . | nindent 4 }} +data: + tls.crt: {{ $cert }} + tls.key: {{ $key }} + ca.crt: {{ $caBundle }} +{{- end }} +{{- if (eq .Values.controlPlane.environment "kubernetes") }} +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + name: {{ include "kuma.name" . }}-admission-mutating-webhook-configuration + namespace: {{ .Release.Namespace }} + labels: {{ include "kuma.cpLabels" . | nindent 4 }} +webhooks: + - name: mesh.defaulter.kuma-admission.kuma.io + admissionReviewVersions: ["v1"] + failurePolicy: Fail + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: NotIn + values: ["kube-system"] + clientConfig: + caBundle: {{ $caBundle }} + service: + namespace: {{ .Release.Namespace }} + name: {{ include "kuma.controlPlane.serviceName" . }} + path: /default-kuma-io-v1alpha1-mesh + rules: + - apiGroups: + - kuma.io + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - meshes + - meshgateways + {{- range $policy, $v := .Values.plugins.policies }} + {{- if $v }} + - {{ $policy }} + {{- end}} + {{- end}} + {{- range $policy, $v := .Values.plugins.resources }} + {{- if $v }} + - {{ $policy }} + {{- end}} + {{- end}} + sideEffects: None + - name: owner-reference.kuma-admission.kuma.io + admissionReviewVersions: ["v1"] + failurePolicy: Fail + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: NotIn + values: ["kube-system"] + clientConfig: + caBundle: {{ $caBundle }} + service: + namespace: {{ .Release.Namespace }} + name: {{ include "kuma.controlPlane.serviceName" . }} + path: /owner-reference-kuma-io-v1alpha1 + rules: + - apiGroups: + - kuma.io + apiVersions: + - v1alpha1 + operations: + - CREATE + resources: + - circuitbreakers + - externalservices + - faultinjections + - healthchecks + - meshgateways + - meshgatewayroutes + - proxytemplates + - ratelimits + - retries + - timeouts + - trafficlogs + - trafficpermissions + - trafficroutes + - traffictraces + - virtualoutbounds + {{- range $policy, $v := .Values.plugins.policies }} + {{- if $v }} + - {{ $policy }} + {{- end}} + {{- end}} + {{- range $policy, $v := .Values.plugins.resources }} + {{- if $v }} + - {{ $policy }} + {{- end}} + {{- end}} + {{ .Values.controlPlane.webhooks.ownerReference.additionalRules | nindent 6 }} + sideEffects: None + {{- if ne .Values.controlPlane.mode "global" }} + - name: namespace-kuma-injector.kuma.io + admissionReviewVersions: ["v1"] + failurePolicy: {{ .Values.controlPlane.injectorFailurePolicy }} + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: NotIn + values: ["kube-system"] + - key: kuma.io/sidecar-injection + operator: In + values: ["enabled", "true"] + clientConfig: + caBundle: {{ $caBundle }} + service: + namespace: {{ .Release.Namespace }} + name: {{ include "kuma.controlPlane.serviceName" . }} + path: /inject-sidecar + rules: + - apiGroups: + - "" + apiVersions: + - v1 + operations: + - CREATE + resources: + - pods + sideEffects: None + - name: pods-kuma-injector.kuma.io + admissionReviewVersions: ["v1"] + failurePolicy: {{ .Values.controlPlane.injectorFailurePolicy }} + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: NotIn + values: ["kube-system"] + objectSelector: + matchLabels: + kuma.io/sidecar-injection: enabled + clientConfig: + caBundle: {{ $caBundle }} + service: + namespace: {{ .Release.Namespace }} + name: {{ include "kuma.controlPlane.serviceName" . }} + path: /inject-sidecar + rules: + - apiGroups: + - "" + apiVersions: + - v1 + operations: + - CREATE + resources: + - pods + sideEffects: None + {{- end }} +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: {{ include "kuma.name" . }}-validating-webhook-configuration + namespace: {{ .Release.Namespace }} + labels: {{ include "kuma.cpLabels" . | nindent 4 }} +webhooks: + - name: validator.kuma-admission.kuma.io + admissionReviewVersions: ["v1"] + failurePolicy: Fail + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: NotIn + values: ["kube-system"] + clientConfig: + caBundle: {{ $caBundle }} + service: + namespace: {{ .Release.Namespace }} + name: {{ include "kuma.controlPlane.serviceName" . }} + path: /validate-kuma-io-v1alpha1 + rules: + - apiGroups: + - kuma.io + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - circuitbreakers + - dataplanes + - externalservices + - faultinjections + - meshgatewayinstances + - healthchecks + - meshes + - meshgateways + - meshgatewayroutes + - proxytemplates + - ratelimits + - retries + - trafficlogs + - trafficpermissions + - trafficroutes + - traffictraces + - virtualoutbounds + - zones + - containerpatches + {{- range $policy, $v := .Values.plugins.policies }} + {{- if $v }} + - {{ $policy }} + {{- end}} + {{- end}} + {{- range $policy, $v := .Values.plugins.resources }} + {{- if $v }} + - {{ $policy }} + {{- end}} + {{- end}} + {{ .Values.controlPlane.webhooks.validator.additionalRules | nindent 6 }} + sideEffects: None + {{- if ne .Values.controlPlane.mode "global" }} + - name: service.validator.kuma-admission.kuma.io + admissionReviewVersions: ["v1"] + failurePolicy: Ignore + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: NotIn + values: ["kube-system"] + clientConfig: + caBundle: {{ $caBundle }} + service: + namespace: {{ .Release.Namespace }} + name: {{ include "kuma.controlPlane.serviceName" . }} + path: /validate-v1-service + rules: + - apiGroups: + - "" + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - services + sideEffects: None + {{- end }} + - name: secret.validator.kuma-admission.kuma.io + admissionReviewVersions: ["v1"] + namespaceSelector: + matchLabels: + kuma.io/system-namespace: "true" + failurePolicy: Ignore + clientConfig: + caBundle: {{ $caBundle }} + service: + namespace: {{ .Release.Namespace }} + name: {{ include "kuma.controlPlane.serviceName" . }} + path: /validate-v1-secret + rules: + - apiGroups: + - "" + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - secrets + sideEffects: None + - name: gateway.validator.kuma-admission.kuma.io + admissionReviewVersions: ["v1"] + failurePolicy: Ignore + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: NotIn + values: ["kube-system"] + clientConfig: + caBundle: {{ $caBundle }} + service: + namespace: {{ .Release.Namespace }} + name: {{ include "kuma.controlPlane.serviceName" . }} + path: /validate-gatewayclass + rules: + - apiGroups: + - "gateway.networking.k8s.io" + apiVersions: + - v1beta1 + operations: + - CREATE + resources: + - gatewayclasses + sideEffects: None +{{- end }} diff --git a/charts/kuma/kuma/2.8.3/templates/egress-deployment.yaml b/charts/kuma/kuma/2.8.3/templates/egress-deployment.yaml new file mode 100644 index 0000000000..7655a3fa7a --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/egress-deployment.yaml @@ -0,0 +1,137 @@ +{{- if .Values.egress.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "kuma.name" . }}-egress + namespace: {{ .Release.Namespace }} + labels: {{ include "kuma.egressLabels" . | nindent 4 }} +spec: + strategy: + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 + {{- if not .Values.egress.autoscaling.enabled }} + replicas: {{ .Values.egress.replicas }} + {{- end }} + selector: + matchLabels: + {{- include "kuma.selectorLabels" . | nindent 6 }} + app: {{ include "kuma.name" . }}-egress + template: + metadata: + annotations: + kuma.io/egress: enabled + {{- range $key, $value := merge .Values.egress.podAnnotations .Values.egress.annotations }} + {{ $key }}: {{ $value | quote }} + {{- end }} + labels: + {{- include "kuma.egressLabels" . | nindent 8 }} + spec: + {{- with .Values.egress.affinity }} + affinity: {{ tpl (toYaml . | nindent 8) $ }} + {{- end }} + {{- with .Values.egress.topologySpreadConstraints }} + topologySpreadConstraints: {{ tpl (toYaml . | nindent 8) $ }} + {{- end }} + securityContext: + {{- toYaml .Values.egress.podSecurityContext | trim | nindent 8 }} + serviceAccountName: {{ include "kuma.name" . }}-egress + automountServiceAccountToken: {{ .Values.egress.automountServiceAccountToken }} + {{- with .Values.egress.nodeSelector }} + nodeSelector: + {{ toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.egress.tolerations }} + tolerations: + {{ toYaml . | nindent 8 }} + {{- end }} + containers: + - name: egress + image: {{ include "kuma.formatImage" (dict "image" .Values.dataPlane.image "root" $) | quote }} + imagePullPolicy: {{ .Values.dataPlane.image.pullPolicy }} + securityContext: + {{- toYaml .Values.egress.containerSecurityContext | trim | nindent 12 }} + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: KUMA_CONTROL_PLANE_URL + value: "https://{{ include "kuma.controlPlane.serviceName" . }}.{{ .Release.Namespace }}:5678" + - name: KUMA_CONTROL_PLANE_CA_CERT_FILE + value: /var/run/secrets/kuma.io/cp-ca/ca.crt + - name: KUMA_DATAPLANE_DRAIN_TIME + value: {{ .Values.egress.drainTime }} + - name: KUMA_DATAPLANE_RUNTIME_TOKEN_PATH + value: /var/run/secrets/kubernetes.io/serviceaccount/token + - name: KUMA_DATAPLANE_PROXY_TYPE + value: "egress" + args: + - run + - --log-level={{ .Values.egress.logLevel | default "info" }} + ports: + - containerPort: 10002 + livenessProbe: + httpGet: + path: "/ready" + port: 9901 + failureThreshold: 12 + initialDelaySeconds: 60 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 3 + readinessProbe: + httpGet: + path: "/ready" + port: 9901 + failureThreshold: 12 + initialDelaySeconds: 1 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 3 + resources: {{ toYaml .Values.egress.resources | nindent 12 }} + volumeMounts: +{{- if not .Values.egress.automountServiceAccountToken }} + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + name: serviceaccount-token + readOnly: true +{{- end }} + - name: control-plane-ca + mountPath: /var/run/secrets/kuma.io/cp-ca + readOnly: true + - name: tmp + mountPath: /tmp + volumes: +{{- if not .Values.egress.automountServiceAccountToken }} + - name: serviceaccount-token + projected: + defaultMode: 420 + sources: + - serviceAccountToken: + expirationSeconds: 3600 + path: token + - configMap: + name: kube-root-ca.crt + items: + - key: ca.crt + path: ca.crt + - downwardAPI: + items: + - fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + path: namespace +{{- end }} + - name: control-plane-ca + secret: + secretName: {{ include "kuma.controlPlane.tls.general.caSecretName" . }} + items: + - key: ca.crt + path: ca.crt + - name: tmp + emptyDir: {} + {{- end }} diff --git a/charts/kuma/kuma/2.8.3/templates/egress-hpa.yaml b/charts/kuma/kuma/2.8.3/templates/egress-hpa.yaml new file mode 100644 index 0000000000..8d4284f417 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/egress-hpa.yaml @@ -0,0 +1,24 @@ +{{- if .Values.egress.autoscaling.enabled }} +{{ if .Capabilities.APIVersions.Has "autoscaling/v2" }} +apiVersion: "autoscaling/v2" +{{ else }} +apiVersion: "autoscaling/v1" +{{ end }} +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "kuma.name" . }}-egress + namespace: {{ .Release.Namespace }} + labels: {{ include "kuma.egressLabels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "kuma.name" . }}-egress + minReplicas: {{ .Values.egress.autoscaling.minReplicas }} + maxReplicas: {{ .Values.egress.autoscaling.maxReplicas }} + {{ if .Capabilities.APIVersions.Has "autoscaling/v2" }} + metrics: {{- toYaml .Values.egress.autoscaling.metrics | nindent 4 }} + {{ else }} + targetCPUUtilizationPercentage: {{ .Values.egress.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/charts/kuma/kuma/2.8.3/templates/egress-pdb.yaml b/charts/kuma/kuma/2.8.3/templates/egress-pdb.yaml new file mode 100644 index 0000000000..ee599003bd --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/egress-pdb.yaml @@ -0,0 +1,20 @@ +{{ if $.Values.egress.podDisruptionBudget.enabled }} +{{ if .Capabilities.APIVersions.Has "policy/v1" }} +apiVersion: policy/v1 +{{ else if .Capabilities.APIVersions.Has "policy/v1beta1" }} +apiVersion: policy/v1beta1 +{{ else }} +{{ fail "pod disruption budgets are not supported by this version of kubernetes" }} +{{ end }} +kind: PodDisruptionBudget +metadata: + name: {{ include "kuma.name" . }}-egress + namespace: {{ .Release.Namespace }} + labels: {{ include "kuma.egressLabels" . | nindent 4 }} +spec: + maxUnavailable: {{ .Values.egress.podDisruptionBudget.maxUnavailable }} + selector: + matchLabels: + {{- include "kuma.selectorLabels" . | nindent 6 }} + app: {{ include "kuma.name" . }}-egress +{{ end }} diff --git a/charts/kuma/kuma/2.8.3/templates/egress-rbac.yaml b/charts/kuma/kuma/2.8.3/templates/egress-rbac.yaml new file mode 100644 index 0000000000..1b4326fdb3 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/egress-rbac.yaml @@ -0,0 +1,18 @@ +{{- if .Values.egress.enabled }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "kuma.name" . }}-egress + namespace: {{ .Release.Namespace }} + labels: {{ include "kuma.egressLabels" . | nindent 4 }} +{{- with .Values.egress.serviceAccountAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} +{{- end }} +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: + {{- range . }} + - name: {{ . | quote }} + {{- end }} +{{- end }} +{{- end }} diff --git a/charts/kuma/kuma/2.8.3/templates/egress-service.yaml b/charts/kuma/kuma/2.8.3/templates/egress-service.yaml new file mode 100644 index 0000000000..2127811fe3 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/egress-service.yaml @@ -0,0 +1,32 @@ +{{- if .Values.egress.enabled }} +{{- if eq .Values.controlPlane.mode "global" }} +{{ fail "You shouldn't run zoneEgress when running the CP in global" }} +{{- end }} +{{- end }} +{{- if and .Values.egress.enabled .Values.egress.service.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "kuma.egress.serviceName" . }} + namespace: {{ .Release.Namespace }} + labels: {{ include "kuma.egressLabels" . | nindent 4 }} + annotations: + {{- range $key, $value := .Values.egress.service.annotations }} + {{ $key }}: {{ $value | quote }} + {{- end }} +spec: + type: {{ .Values.egress.service.type }} + {{- if .Values.egress.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.egress.service.loadBalancerIP }} + {{- end }} + ports: + - port: {{ .Values.egress.service.port }} + protocol: TCP + targetPort: 10002 + {{- if and (eq .Values.egress.service.type "NodePort") .Values.egress.service.nodePort }} + nodePort: {{ .Values.egress.service.nodePort }} + {{- end }} + selector: + app: {{ include "kuma.name" . }}-egress + {{- include "kuma.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/charts/kuma/kuma/2.8.3/templates/gateway-class.yaml b/charts/kuma/kuma/2.8.3/templates/gateway-class.yaml new file mode 100644 index 0000000000..cf1ae305df --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/gateway-class.yaml @@ -0,0 +1,19 @@ +{{- if and (eq .Values.controlPlane.environment "kubernetes") (eq .Values.controlPlane.mode "zone") }} +{{- if .Capabilities.APIVersions.Has "gateway.networking.k8s.io/v1/GatewayClass" }} +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: GatewayClass +metadata: + name: kuma +spec: + controllerName: "gateways.kuma.io/controller" +{{- else if .Capabilities.APIVersions.Has "gateway.networking.k8s.io/v1beta1/GatewayClass" }} +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: GatewayClass +metadata: + name: kuma +spec: + controllerName: "gateways.kuma.io/controller" +{{- end }} +{{- end }} diff --git a/charts/kuma/kuma/2.8.3/templates/ingress-deployment.yaml b/charts/kuma/kuma/2.8.3/templates/ingress-deployment.yaml new file mode 100644 index 0000000000..65ab4e19af --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/ingress-deployment.yaml @@ -0,0 +1,141 @@ +{{- if .Values.ingress.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "kuma.name" . }}-ingress + namespace: {{ .Release.Namespace }} + labels: {{ include "kuma.ingressLabels" . | nindent 4 }} +spec: + strategy: + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 + {{- if not .Values.ingress.autoscaling.enabled }} + replicas: {{ .Values.ingress.replicas }} + {{- end }} + selector: + matchLabels: + {{- include "kuma.selectorLabels" . | nindent 6 }} + app: {{ include "kuma.name" . }}-ingress + template: + metadata: + annotations: + kuma.io/ingress: enabled + {{- range $key, $value := merge .Values.ingress.podAnnotations .Values.ingress.annotations }} + {{ $key }}: {{ $value | quote }} + {{- end }} + labels: + {{- include "kuma.ingressLabels" . | nindent 8 }} + spec: + {{- with .Values.ingress.affinity }} + affinity: {{ tpl (toYaml . | nindent 8) $ }} + {{- end }} + {{- with .Values.ingress.topologySpreadConstraints }} + topologySpreadConstraints: {{ tpl (toYaml . | nindent 8) $ }} + {{- end }} + securityContext: + {{- toYaml .Values.ingress.podSecurityContext | trim | nindent 8 }} + serviceAccountName: {{ include "kuma.name" . }}-ingress + automountServiceAccountToken: {{ .Values.ingress.automountServiceAccountToken }} + {{- with .Values.ingress.nodeSelector }} + nodeSelector: + {{ toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.ingress.tolerations }} + tolerations: + {{ toYaml . | nindent 8 }} + {{- end }} + terminationGracePeriodSeconds: {{ .Values.ingress.terminationGracePeriodSeconds }} + containers: + - name: ingress + image: {{ include "kuma.formatImage" (dict "image" .Values.dataPlane.image "root" $) | quote }} + imagePullPolicy: {{ .Values.dataPlane.image.pullPolicy }} + securityContext: + {{- toYaml .Values.ingress.containerSecurityContext | trim | nindent 12 }} + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: KUMA_CONTROL_PLANE_URL + value: "https://{{ include "kuma.controlPlane.serviceName" . }}.{{ .Release.Namespace }}:5678" + - name: KUMA_CONTROL_PLANE_CA_CERT_FILE + value: /var/run/secrets/kuma.io/cp-ca/ca.crt + - name: KUMA_DATAPLANE_DRAIN_TIME + value: {{ .Values.ingress.drainTime }} + - name: KUMA_DATAPLANE_RUNTIME_TOKEN_PATH + value: /var/run/secrets/kubernetes.io/serviceaccount/token + - name: KUMA_DATAPLANE_PROXY_TYPE + value: "ingress" + args: + - run + - --log-level={{ .Values.ingress.logLevel | default "info" }} + ports: + - containerPort: 10001 + livenessProbe: + httpGet: + path: "/ready" + port: 9901 + failureThreshold: 12 + initialDelaySeconds: 60 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 3 + readinessProbe: + httpGet: + path: "/ready" + port: 9901 + failureThreshold: 12 + initialDelaySeconds: 1 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 3 + resources: {{ toYaml .Values.ingress.resources | nindent 12 }} + {{ with .Values.ingress.lifecycle}} + lifecycle: {{ . | toYaml | nindent 12 }} + {{ end }} + volumeMounts: +{{- if not .Values.ingress.automountServiceAccountToken }} + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + name: serviceaccount-token + readOnly: true +{{- end }} + - name: control-plane-ca + mountPath: /var/run/secrets/kuma.io/cp-ca + readOnly: true + - name: tmp + mountPath: /tmp + volumes: +{{- if not .Values.ingress.automountServiceAccountToken }} + - name: serviceaccount-token + projected: + defaultMode: 420 + sources: + - serviceAccountToken: + expirationSeconds: 3600 + path: token + - configMap: + name: kube-root-ca.crt + items: + - key: ca.crt + path: ca.crt + - downwardAPI: + items: + - fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + path: namespace +{{- end }} + - name: control-plane-ca + secret: + secretName: {{ include "kuma.controlPlane.tls.general.caSecretName" . }} + items: + - key: ca.crt + path: ca.crt + - name: tmp + emptyDir: {} +{{- end }} diff --git a/charts/kuma/kuma/2.8.3/templates/ingress-hpa.yaml b/charts/kuma/kuma/2.8.3/templates/ingress-hpa.yaml new file mode 100644 index 0000000000..4aaeabe672 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/ingress-hpa.yaml @@ -0,0 +1,24 @@ +{{- if .Values.ingress.autoscaling.enabled }} +{{ if .Capabilities.APIVersions.Has "autoscaling/v2" }} +apiVersion: "autoscaling/v2" +{{ else }} +apiVersion: "autoscaling/v1" +{{ end }} +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "kuma.name" . }}-ingress + namespace: {{ .Release.Namespace }} + labels: {{ include "kuma.ingressLabels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "kuma.name" . }}-ingress + minReplicas: {{ .Values.ingress.autoscaling.minReplicas }} + maxReplicas: {{ .Values.ingress.autoscaling.maxReplicas }} + {{ if .Capabilities.APIVersions.Has "autoscaling/v2" }} + metrics: {{- toYaml .Values.ingress.autoscaling.metrics | nindent 4 }} + {{ else }} + targetCPUUtilizationPercentage: {{ .Values.ingress.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/charts/kuma/kuma/2.8.3/templates/ingress-pdb.yaml b/charts/kuma/kuma/2.8.3/templates/ingress-pdb.yaml new file mode 100644 index 0000000000..639d1b5746 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/ingress-pdb.yaml @@ -0,0 +1,20 @@ +{{ if $.Values.ingress.podDisruptionBudget.enabled }} +{{ if .Capabilities.APIVersions.Has "policy/v1" }} +apiVersion: policy/v1 +{{ else if .Capabilities.APIVersions.Has "policy/v1beta1" }} +apiVersion: policy/v1beta1 +{{ else }} +{{ fail "pod disruption budgets are not supported by this version of kubernetes" }} +{{ end }} +kind: PodDisruptionBudget +metadata: + name: {{ include "kuma.name" . }}-ingress + namespace: {{ .Release.Namespace }} + labels: {{ include "kuma.ingressLabels" . | nindent 4 }} +spec: + maxUnavailable: {{ .Values.ingress.podDisruptionBudget.maxUnavailable }} + selector: + matchLabels: + {{- include "kuma.selectorLabels" . | nindent 6 }} + app: {{ include "kuma.name" . }}-ingress +{{ end }} diff --git a/charts/kuma/kuma/2.8.3/templates/ingress-rbac.yaml b/charts/kuma/kuma/2.8.3/templates/ingress-rbac.yaml new file mode 100644 index 0000000000..e4e1d61ced --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/ingress-rbac.yaml @@ -0,0 +1,18 @@ +{{- if .Values.ingress.enabled }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "kuma.name" . }}-ingress + namespace: {{ .Release.Namespace }} + labels: {{ include "kuma.ingressLabels" . | nindent 4 }} +{{- with .Values.ingress.serviceAccountAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} +{{- end }} +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: + {{- range . }} + - name: {{ . | quote }} + {{- end }} +{{- end }} +{{- end }} diff --git a/charts/kuma/kuma/2.8.3/templates/ingress-service.yaml b/charts/kuma/kuma/2.8.3/templates/ingress-service.yaml new file mode 100644 index 0000000000..74a4dde907 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/ingress-service.yaml @@ -0,0 +1,32 @@ +{{- if .Values.ingress.enabled }} +{{- if or (eq .Values.controlPlane.mode "global") (eq .Values.controlPlane.mode "standalone") }} +{{ fail "You shouldn't run zoneIngress when running the CP in global or standalone" }} +{{- end }} +{{- end }} +{{- if and .Values.ingress.enabled .Values.ingress.service.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "kuma.ingress.serviceName" . }} + namespace: {{ .Release.Namespace }} + labels: {{ include "kuma.ingressLabels" . | nindent 4 }} + annotations: + {{- range $key, $value := .Values.ingress.service.annotations }} + {{ $key }}: {{ $value | quote }} + {{- end }} +spec: + type: {{ .Values.ingress.service.type }} + {{- if .Values.ingress.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.ingress.service.loadBalancerIP }} + {{- end }} + ports: + - port: {{ .Values.ingress.service.port }} + protocol: TCP + targetPort: 10001 + {{- if and (eq .Values.ingress.service.type "NodePort") .Values.ingress.service.nodePort }} + nodePort: {{ .Values.ingress.service.nodePort }} + {{- end }} + selector: + app: {{ include "kuma.name" . }}-ingress + {{- include "kuma.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/charts/kuma/kuma/2.8.3/templates/post-delete-cleanup-ebpf-job.yaml b/charts/kuma/kuma/2.8.3/templates/post-delete-cleanup-ebpf-job.yaml new file mode 100644 index 0000000000..aaa3166ff7 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/post-delete-cleanup-ebpf-job.yaml @@ -0,0 +1,126 @@ +{{- if and (.Values.experimental.ebpf.enabled) (and (not .Values.cni.enabled) (not .Values.noHelmHooks) (eq .Values.controlPlane.environment "kubernetes")) }} + {{- $serviceAccountName := printf "%s-cleanup-node-ebpf-job" (include "kuma.name" .) }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ $serviceAccountName }} + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": "post-delete" + "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded,hook-failed" + labels: + {{- include "kuma.labels" . | nindent 4 }} +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: + {{- range . }} + - name: {{ . | quote }} + {{- end }} +{{- end }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "kuma.name" . }}-cleanup-node-ebpf-job + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": "post-delete" + "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded,hook-failed" + labels: + {{- include "kuma.labels" . | nindent 4 }} +rules: + - apiGroups: [""] + resources: + - nodes + verbs: + - list + - apiGroups: [""] + resources: + - pods + verbs: + - watch + - delete + - deletecollection + - apiGroups: ["batch"] + resources: + - jobs + verbs: + - watch + - create + - delete + - deletecollection +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "kuma.name" . }}-cleanup-node-ebpf-job + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": "post-delete" + "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded,hook-failed" + labels: + {{- include "kuma.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "kuma.name" . }}-cleanup-node-ebpf-job +subjects: + - kind: ServiceAccount + name: {{ $serviceAccountName }} + namespace: {{ .Release.Namespace }} +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "kuma.name" . }}-cleanup-node-ebpf-job + namespace: {{ .Release.Namespace }} + labels: + {{ include "kuma.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": "post-delete" + {{/* Ensure the job is created after the RBAC resources */}} + "helm.sh/hook-weight": "5" + "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded,hook-failed" +spec: + template: + metadata: + name: {{ template "kuma.name" . }}-cleanup-node-ebpf-job + labels: + {{ include "kuma.labels" . | nindent 8 }} + spec: + serviceAccountName: {{ $serviceAccountName }} + {{- with .Values.hooks.nodeSelector }} + nodeSelector: + {{ toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.hooks.tolerations }} + tolerations: + {{ toYaml . | nindent 8 }} + {{- end }} + restartPolicy: OnFailure + {{- if .Values.hooks.ebpfCleanup.podSecurityContext }} + securityContext: + {{ toYaml .Values.hooks.ebpfCleanup.podSecurityContext | trim | nindent 8 }} + {{- end }} + containers: + - name: post-delete-job + image: {{ include "kuma.formatImage" (dict "image" .Values.dataPlane.initImage "root" $) | quote }} + {{- if .Values.hooks.ebpfCleanup.containerSecurityContext }} + securityContext: + {{ toYaml .Values.hooks.ebpfCleanup.containerSecurityContext | trim | nindent 12 }} + {{- end }} + resources: + requests: + cpu: "20m" + memory: "20Mi" + limits: + cpu: "40m" + memory: "40Mi" + command: + - 'kumactl' + - 'uninstall' + - 'ebpf' + - '--cleanup-image-registry' + - {{ .Values.global.image.registry }} + - '--cleanup-image-repository' + - {{ .Values.dataPlane.initImage.repository }} + {{- end }} diff --git a/charts/kuma/kuma/2.8.3/templates/pre-delete-webhooks.yaml b/charts/kuma/kuma/2.8.3/templates/pre-delete-webhooks.yaml new file mode 100644 index 0000000000..e6948af2f5 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/pre-delete-webhooks.yaml @@ -0,0 +1,109 @@ +{{- if and (eq .Values.controlPlane.environment "kubernetes") (not .Values.noHelmHooks) }} +# HELM first deletes RBAC of Kuma, then it tries to delete Secrets. We've got validating webhook on Secrets. +# But even that the policy of this webhook is Ignore, it fails because Kuma does not have permission to access Secrets anymore. +# Therefore we first need to delete webhook so we can delete the rest of the deployment +{{- $serviceAccountName := printf "%s-pre-delete-job" (include "kuma.name" .) }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ $serviceAccountName }} + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": "pre-delete" + "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded,hook-failed" + labels: + {{- include "kuma.labels" . | nindent 4 }} +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: + {{- range . }} + - name: {{ . | quote }} + {{- end }} +{{- end }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "kuma.name" . }}-pre-delete-job + annotations: + "helm.sh/hook": "pre-delete" + "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded,hook-failed" + labels: + {{- include "kuma.labels" . | nindent 4 }} +rules: + - apiGroups: + - admissionregistration.k8s.io + resources: + - validatingwebhookconfigurations + resourceNames: + - {{ include "kuma.name" . }}-validating-webhook-configuration + verbs: + - delete +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "kuma.name" . }}-pre-delete-job + annotations: + "helm.sh/hook": "pre-delete" + "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded,hook-failed" + labels: + {{- include "kuma.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "kuma.name" . }}-pre-delete-job +subjects: + - kind: ServiceAccount + name: {{ $serviceAccountName }} + namespace: {{ .Release.Namespace }} +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "kuma.name" . }}-delete-webhook + namespace: {{ .Release.Namespace }} + labels: + {{ include "kuma.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": "pre-delete" + {{/* Ensure the job is created after the RBAC resources */}} + "helm.sh/hook-weight": "5" + "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded,hook-failed" +spec: + template: + metadata: + name: {{ template "kuma.name" . }}-delete-webhook + labels: + {{ include "kuma.labels" . | nindent 8 }} + spec: + serviceAccountName: {{ $serviceAccountName }} + {{- with .Values.hooks.nodeSelector }} + nodeSelector: + {{ toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.hooks.tolerations }} + tolerations: + {{ toYaml . | nindent 8 }} + {{- end }} + restartPolicy: OnFailure + securityContext: + {{- toYaml .Values.hooks.podSecurityContext | trim | nindent 8 }} + containers: + - name: pre-delete-job + image: "{{ .Values.kubectl.image.registry }}/{{ .Values.kubectl.image.repository }}:{{ .Values.kubectl.image.tag }}" + command: + - 'kubectl' + - 'delete' + - 'ValidatingWebhookConfiguration' + - '--ignore-not-found' + - {{ include "kuma.name" . }}-validating-webhook-configuration + securityContext: + {{- toYaml (mergeOverwrite (dict "runAsUser" 65534) .Values.hooks.containerSecurityContext) | trim | nindent 12 }} + resources: + requests: + cpu: "100m" + memory: "256Mi" + limits: + cpu: "100m" + memory: "256Mi" +{{- end }} diff --git a/charts/kuma/kuma/2.8.3/templates/pre-install-patch-namespace-job.yaml b/charts/kuma/kuma/2.8.3/templates/pre-install-patch-namespace-job.yaml new file mode 100644 index 0000000000..a84d7accfd --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/pre-install-patch-namespace-job.yaml @@ -0,0 +1,124 @@ +{{- if and ( .Values.noHelmHooks ) (eq .Values.controlPlane.environment "kubernetes") }} + {{- $errorMessage := ".Values.noHelmHooks is set. You must manually create and label the system namespace with kuma.io/system-namespace: \"true\" before installing or upgrading the chart" }} + {{- $systemNamespace := (lookup "v1" "Namespace" "" .Release.Namespace) }} + {{- if not $systemNamespace }} + {{- fail $errorMessage }} + {{- end }} + {{- $systemNamespaceLabels := ($systemNamespace).metadata.labels }} + {{- if ne (get $systemNamespaceLabels "kuma.io/system-namespace") "true" }} + {{- fail $errorMessage }} + {{- end }} +{{- else}} + {{- if .Values.patchSystemNamespace }} + {{- $serviceAccountName := printf "%s-patch-ns-job" (include "kuma.name" .) }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ $serviceAccountName }} + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": "pre-install" + "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded,hook-failed" + labels: + {{- include "kuma.labels" . | nindent 4 }} +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: + {{- range . }} + - name: {{ . | quote }} + {{- end }} +{{- end }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "kuma.name" . }}-patch-ns-job + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": "pre-install" + "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded,hook-failed" + labels: + {{- include "kuma.labels" . | nindent 4 }} +rules: + - apiGroups: + - "" + resources: + - namespaces + resourceNames: + - {{ .Release.Namespace }} + verbs: + - get + - patch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "kuma.name" . }}-patch-ns-job + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": "pre-install" + "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded,hook-failed" + labels: + {{- include "kuma.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "kuma.name" . }}-patch-ns-job +subjects: + - kind: ServiceAccount + name: {{ $serviceAccountName }} + namespace: {{ .Release.Namespace }} +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "kuma.name" . }}-patch-ns + namespace: {{ .Release.Namespace }} + labels: + {{ include "kuma.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": "pre-install" + {{/* Ensure the job is created after the RBAC resources */}} + "helm.sh/hook-weight": "5" + "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded,hook-failed" +spec: + template: + metadata: + name: {{ template "kuma.name" . }}-patch-ns-script + labels: + {{ include "kuma.labels" . | nindent 8 }} + spec: + serviceAccountName: {{ $serviceAccountName }} + {{- with .Values.hooks.nodeSelector }} + nodeSelector: + {{ toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.hooks.tolerations }} + tolerations: + {{ toYaml . | nindent 8 }} + {{- end }} + restartPolicy: OnFailure + securityContext: + {{- toYaml .Values.hooks.podSecurityContext | trim | nindent 8 }} + containers: + - name: pre-install-job + image: "{{ .Values.kubectl.image.registry }}/{{ .Values.kubectl.image.repository }}:{{ .Values.kubectl.image.tag }}" + securityContext: + {{- toYaml (mergeOverwrite (dict "runAsUser" 65534) .Values.hooks.containerSecurityContext) | trim | nindent 12 }} + resources: + requests: + cpu: "100m" + memory: "256Mi" + limits: + cpu: "100m" + memory: "256Mi" + command: + - 'kubectl' + - 'patch' + - 'namespace' + - {{ .Release.Namespace | quote }} + - '--type' + - 'merge' + - '--patch' + - '{ "metadata": { "labels": { "kuma.io/system-namespace": "true" } } }' + {{- end }} +{{- end }} diff --git a/charts/kuma/kuma/2.8.3/templates/pre-upgrade-install-crds-job.yaml b/charts/kuma/kuma/2.8.3/templates/pre-upgrade-install-crds-job.yaml new file mode 100644 index 0000000000..8fadf17227 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/templates/pre-upgrade-install-crds-job.yaml @@ -0,0 +1,171 @@ +{{- if (and .Values.installCrdsOnUpgrade.enabled (and (not .Values.noHelmHooks) (eq .Values.controlPlane.environment "kubernetes"))) }} + {{ $hook := "pre-upgrade,pre-install" }} + {{- $serviceAccountName := printf "%s-install-crds" (include "kuma.name" .) }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ $serviceAccountName }} + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": "{{ $hook }}" + "helm.sh/hook-weight": "-1" + "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded,hook-failed" + labels: + {{- include "kuma.labels" . | nindent 4 }} +{{- with concat .Values.installCrdsOnUpgrade.imagePullSecrets .Values.global.imagePullSecrets | uniq }} +imagePullSecrets: + {{- range . }} + - name: {{ . | quote }} + {{- end }} +{{- end }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "kuma.name" . }}-install-crds + annotations: + "helm.sh/hook": "{{ $hook }}" + "helm.sh/hook-weight": "-1" + "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded,hook-failed" + labels: + {{- include "kuma.labels" . | nindent 4 }} +rules: + - apiGroups: + - "apiextensions.k8s.io" + resources: + - customresourcedefinitions + verbs: + - create + - patch + - update + - list + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "kuma.name" . }}-install-crds + annotations: + "helm.sh/hook": "{{ $hook }}" + "helm.sh/hook-weight": "-1" + "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded,hook-failed" + labels: + {{- include "kuma.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "kuma.name" . }}-install-crds +subjects: + - kind: ServiceAccount + name: {{ $serviceAccountName }} + namespace: {{ .Release.Namespace }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "kuma.name" . }}-install-crds-scripts + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": "{{ $hook }}" + "helm.sh/hook-weight": "-1" + "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded" + labels: + {{- include "kuma.labels" . | nindent 4 }} +data: + install_crds.sh: | + #!/usr/bin/env sh + set -e + + if [ -s /kuma/crds/crds.yaml ]; then + echo "/kuma/crds/crds.yaml found and is not empty, adding crds" + kubectl apply -f /kuma/crds/crds.yaml + else + echo "/kuma/crds/crds.yaml not found or empty, it looks like there is no crds to install" + fi + save_crds.sh: | + set -e + + crds="$(kumactl install crds --no-config)" + + if [ -n "${crds}" ]; then + echo "found crds - saving to /kuma/crds/crds.yaml" + echo "${crds}" > /kuma/crds/crds.yaml + fi +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "kuma.name" . }}-install-crds + namespace: {{ .Release.Namespace }} + labels: + {{ include "kuma.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": "{{ $hook }}" + "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded" +spec: + template: + metadata: + name: {{ template "kuma.name" . }}-install-crds-job + labels: + {{ include "kuma.labels" . | nindent 8 }} + spec: + serviceAccountName: {{ $serviceAccountName }} + {{- with .Values.hooks.nodeSelector }} + nodeSelector: + {{ toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.hooks.tolerations }} + tolerations: + {{ toYaml . | nindent 8 }} + {{- end }} + restartPolicy: OnFailure + securityContext: + {{- toYaml .Values.hooks.podSecurityContext | trim | nindent 8 }} + containers: + - name: pre-upgrade-job + image: "{{ .Values.kubectl.image.registry }}/{{ .Values.kubectl.image.repository }}:{{ .Values.kubectl.image.tag }}" + securityContext: + {{- toYaml (mergeOverwrite (dict "runAsUser" 65534) .Values.hooks.containerSecurityContext) | trim | nindent 12 }} + resources: + requests: + cpu: "100m" + memory: "256Mi" + limits: + cpu: "100m" + memory: "256Mi" + command: ["/kuma/scripts/install_crds.sh"] + volumeMounts: + - mountPath: /kuma/crds + name: crds + readOnly: true + - mountPath: /kuma/scripts + name: scripts + readOnly: true + initContainers: + - name: pre-upgrade-job-init + image: {{ include "kuma.formatImage" (dict "image" .Values.kumactl.image "root" $) | quote }} + securityContext: + {{- toYaml .Values.hooks.containerSecurityContext | trim | nindent 12 }} + resources: + requests: + cpu: "100m" + memory: "256Mi" + limits: + cpu: "100m" + memory: "256Mi" + volumeMounts: + - mountPath: /kuma/crds + name: crds + - mountPath: /kuma/scripts + name: scripts + readOnly: true + command: ["sh", "-c"] + args: ["/kuma/scripts/save_crds.sh"] + volumes: + - name: scripts + configMap: + name: {{ include "kuma.name" . }}-install-crds-scripts + defaultMode: 0755 + - name: crds + emptyDir: {} +{{- end }} diff --git a/charts/kuma/kuma/2.8.3/values.yaml b/charts/kuma/kuma/2.8.3/values.yaml new file mode 100644 index 0000000000..bcd67e4546 --- /dev/null +++ b/charts/kuma/kuma/2.8.3/values.yaml @@ -0,0 +1,748 @@ +global: + image: + # -- Default registry for all Kuma Images + registry: "docker.io/kumahq" + # -- The default tag for all Kuma images, which itself defaults to .Chart.AppVersion + tag: + # -- Add `imagePullSecrets` to all the service accounts used for Kuma components + imagePullSecrets: [] + +# -- Whether to patch the target namespace with the system label +patchSystemNamespace: true + +installCrdsOnUpgrade: + # -- Whether install new CRDs before upgrade (if any were introduced with the new version of Kuma) + enabled: true + # -- The `imagePullSecrets` to attach to the Service Account running CRD installation. + # This field will be deprecated in a future release, please use .global.imagePullSecrets + imagePullSecrets: [] + +# -- Whether to disable all helm hooks +noHelmHooks: false + +# -- Whether to restart control-plane by calculating a new checksum for the secret +restartOnSecretChange: true + +controlPlane: + # -- Environment that control plane is run in, useful when running universal global control plane on k8s + environment: "kubernetes" + + # -- Labels to add to resources in addition to default labels + extraLabels: {} + + # -- Kuma CP log level: one of off,info,debug + logLevel: "info" + + # -- Kuma CP log output path: Defaults to /dev/stdout + logOutputPath: "" + + # -- Kuma CP modes: one of zone,global + mode: "zone" + + # -- (string) Kuma CP zone, if running multizone + zone: + + # -- Only used in `zone` mode + kdsGlobalAddress: "" + + # -- Number of replicas of the Kuma CP. Ignored when autoscaling is enabled + replicas: 1 + + # -- Minimum number of seconds for which a newly created pod should be ready for it to be considered available. + minReadySeconds: 0 + + # -- Annotations applied only to the `Deployment` resource + deploymentAnnotations: {} + + # -- Annotations applied only to the `Pod` resource + podAnnotations: {} + + # Horizontal Pod Autoscaling configuration + autoscaling: + # -- Whether to enable Horizontal Pod Autoscaling, which requires the [Metrics Server](https://github.com/kubernetes-sigs/metrics-server) in the cluster + enabled: false + + # -- The minimum CP pods to allow + minReplicas: 2 + # -- The max CP pods to scale to + maxReplicas: 5 + + # -- For clusters that don't support autoscaling/v2, autoscaling/v1 is used + targetCPUUtilizationPercentage: 80 + # -- For clusters that do support autoscaling/v2, use metrics + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 80 + + # -- Node selector for the Kuma Control Plane pods + nodeSelector: + kubernetes.io/os: linux + + # -- Tolerations for the Kuma Control Plane pods + tolerations: [] + + podDisruptionBudget: + # -- Whether to create a pod disruption budget + enabled: false + # -- The maximum number of unavailable pods allowed by the budget + maxUnavailable: 1 + + # -- Affinity placement rule for the Kuma Control Plane pods. + # This is rendered as a template, so you can reference other helm variables or includes. + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + # These match the selector labels used on the deployment. + matchExpressions: + - key: app.kubernetes.io/name + operator: In + values: + - '{{ include "kuma.name" . }}' + - key: app.kubernetes.io/instance + operator: In + values: + - '{{ .Release.Name }}' + - key: app + operator: In + values: + - '{{ include "kuma.name" . }}-control-plane' + topologyKey: kubernetes.io/hostname + + # -- Topology spread constraints rule for the Kuma Control Plane pods. + # This is rendered as a template, so you can use variables to generate match labels. + topologySpreadConstraints: + + # -- Failure policy of the mutating webhook implemented by the Kuma Injector component + injectorFailurePolicy: Fail + + service: + apiServer: + http: + # -- Port on which Http api server Service is exposed on Node for service of type NodePort + nodePort: 30681 + https: + # -- Port on which Https api server Service is exposed on Node for service of type NodePort + nodePort: 30682 + + # -- Whether to create a service resource. + enabled: true + + # -- (string) Optionally override of the Kuma Control Plane Service's name + name: + + # -- Service type of the Kuma Control Plane + type: ClusterIP + + # -- Annotations to put on the Kuma Control Plane + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "5680" + + # Kuma API and GUI ingress settings. Useful if you want to expose the + # API and GUI of Kuma outside the k8s cluster. + ingress: + # -- Install K8s Ingress resource that exposes GUI and API + enabled: false + # -- IngressClass defines which controller will implement the resource + ingressClassName: + # -- Ingress hostname + hostname: + # -- Map of ingress annotations. + annotations: {} + # -- Ingress path. + path: / + # -- Each path in an Ingress is required to have a corresponding path type. (ImplementationSpecific/Exact/Prefix) + pathType: ImplementationSpecific + # -- Port from kuma-cp to use to expose API and GUI. Switch to 5682 to expose TLS port + servicePort: 5681 + + globalZoneSyncService: + # -- Whether to create a k8s service for the global zone sync + # service. It will only be created when enabled and deploying the global + # control plane. + enabled: true + # -- Service type of the Global-zone sync + type: LoadBalancer + # -- (string) Optionally specify IP to be used by cloud provider when configuring load balancer + loadBalancerIP: + # -- Optionally specify allowed source ranges that can access the load balancer + loadBalancerSourceRanges: [] + # -- Additional annotations to put on the Global Zone Sync Service + annotations: { } + # -- Port on which Global Zone Sync Service is exposed on Node for service of type NodePort + nodePort: 30685 + # -- Port on which Global Zone Sync Service is exposed + port: 5685 + # -- Protocol of the Global Zone Sync service port + protocol: grpc + + defaults: + # -- Whether to skip creating the default Mesh + skipMeshCreation: false + + # -- Whether to automountServiceAccountToken for cp. Optionally set to false + automountServiceAccountToken: true + + # -- Optionally override the resource spec + resources: + requests: + cpu: 500m + memory: 256Mi + limits: + memory: 256Mi + + # -- Pod lifecycle settings (useful for adding a preStop hook, when + # using AWS ALB or NLB) + lifecycle: {} + + # -- Number of seconds to wait before force killing the pod. Make sure to + # update this if you add a preStop hook. + terminationGracePeriodSeconds: 30 + + # TLS for various servers + tls: + general: + # -- Secret that contains tls.crt, tls.key [and ca.crt when no + # controlPlane.tls.general.caSecretName specified] for protecting + # Kuma in-cluster communication + secretName: "" + # -- Secret that contains ca.crt that was used to sign cert for protecting + # Kuma in-cluster communication (ca.crt present in this secret + # have precedence over the one provided in the controlPlane.tls.general.secretName) + caSecretName: "" + # -- Base64 encoded CA certificate (the same as in controlPlane.tls.general.secret#ca.crt) + caBundle: "" + apiServer: + # -- Secret that contains tls.crt, tls.key for protecting Kuma API on HTTPS + secretName: "" + # -- Secret that contains list of .pem certificates that can access admin endpoints of Kuma API on HTTPS + clientCertsSecretName: "" + # - if not creating the global control plane, then do nothing + # - if secretName is empty and create is false, then do nothing + # - if secretName is non-empty and create is false, then use the secret made outside of helm with the name secretName + # - if secretName is empty and create is true, then create a secret with a default name and use it + # - if secretName is non-empty and create is true, then create the secret using the provided name + kdsGlobalServer: + # -- Name of the K8s TLS Secret resource. If you set this and don't set + # create=true, you have to create the secret manually. + secretName: "" + # -- Whether to create the TLS secret in helm. + create: false + # -- The TLS certificate to offer. + cert: "" + # -- The TLS key to use. + key: "" + # - if not creating the zonal control plane, then do nothing + # - if secretName is empty and create is false, then do nothing + # - if secretName is non-empty and create is false, then use the secret made outside of helm with the name secretName + # - if secretName is empty and create is true, then create a secret with a default name and use it + # - if secretName is non-empty and create is true, then create the secret using the provided name + kdsZoneClient: + # -- Name of the K8s Secret resource that contains ca.crt which was + # used to sign the certificate of KDS Global Server. If you set this + # and don't set create=true, you have to create the secret manually. + secretName: "" + # -- Whether to create the TLS secret in helm. + create: false + # -- CA bundle that was used to sign the certificate of KDS Global Server. + cert: "" + # -- If true, TLS cert of the server is not verified. + skipVerify: false + + # -- Annotations to add for Control Plane's Service Account + serviceAccountAnnotations: { } + + image: + # -- Kuma CP ImagePullPolicy + pullPolicy: IfNotPresent + # -- Kuma CP image repository + repository: "kuma-cp" + # -- Kuma CP Image tag. When not specified, the value is copied from global.tag + tag: + + # -- (object with { Env: string, Secret: string, Key: string }) Secrets to add as environment variables, + # where `Env` is the name of the env variable, + # `Secret` is the name of the Secret, + # and `Key` is the key of the Secret value to use + secrets: + # someSecret: + # Secret: some-secret + # Key: secret_key + # Env: SOME_SECRET + + # -- Additional environment variables that will be passed to the control plane + envVars: { } + + # -- Additional config maps to mount into the control plane, with optional inline values + extraConfigMaps: [ ] +# - name: extra-config +# mountPath: /etc/extra-config +# readOnly: true +# values: +# extra-config-key: | +# extra-config-value + + # -- (object with { name: string, mountPath: string, readOnly: string }) Additional secrets to mount into the control plane, + # where `Env` is the name of the env variable, + # `Secret` is the name of the Secret, + # and `Key` is the key of the Secret value to use + extraSecrets: + # extraConfig: + # name: extra-config + # mountPath: /etc/extra-config + # readOnly: true + + webhooks: + validator: + # -- Additional rules to apply on Kuma validator webhook. Useful when building custom policy on top of Kuma. + additionalRules: "" + ownerReference: + # -- Additional rules to apply on Kuma owner reference webhook. Useful when building custom policy on top of Kuma. + additionalRules: "" + + # -- Specifies if the deployment should be started in hostNetwork mode. + hostNetwork: false + # -- Define a new server port for the admission controller. Recommended to set in combination with + # hostNetwork to prevent multiple port bindings on the same port (like Calico in AWS EKS). + admissionServerPort: 5443 + + # -- Security context at the pod level for control plane. + podSecurityContext: + runAsNonRoot: true + + # -- Security context at the container level for control plane. + containerSecurityContext: + readOnlyRootFilesystem: true + + # -- If true, then control plane can support TLS secrets for builtin gateway outside of mesh system namespace. + # The downside is that control plane requires permission to read Secrets in all namespaces. + supportGatewaySecretsInAllNamespaces: false + +cni: + # -- Install Kuma with CNI instead of proxy init container + enabled: false + # -- Install CNI in chained mode + chained: false + # -- Set the CNI install directory + netDir: /etc/cni/multus/net.d + # -- Set the CNI bin directory + binDir: /var/lib/cni/bin + # -- Set the CNI configuration name + confName: kuma-cni.conf + # -- CNI log level: one of off,info,debug + logLevel: info + # -- Node Selector for the CNI pods + nodeSelector: + kubernetes.io/os: linux + # -- Tolerations for the CNI pods + tolerations: [] + # -- Additional pod annotations + podAnnotations: { } + # -- Set the CNI namespace + namespace: kube-system + + image: + # -- CNI image repository + repository: "kuma-cni" + # -- CNI image tag - defaults to .Chart.AppVersion + tag: + # -- CNI image pull policy + imagePullPolicy: IfNotPresent + + # -- it's only useful in tests to trigger a possible race condition + delayStartupSeconds: 0 + + # -- use new CNI (experimental) + experimental: + imageEbpf: + # -- CNI experimental eBPF image registry + registry: "docker.io/kumahq" + # -- CNI experimental eBPF image repository + repository: "merbridge" + # -- CNI experimental eBPF image tag + tag: "0.8.5" + + resources: + requests: + cpu: 100m + memory: 100Mi + limits: + memory: 100Mi + + # -- Security context at the pod level for cni + podSecurityContext: {} + + # -- Security context at the container level for cni + containerSecurityContext: + readOnlyRootFilesystem: true + runAsNonRoot: false + runAsUser: 0 + runAsGroup: 0 + +dataPlane: + # -- If true, then turn on CoreDNS query logging + dnsLogging: false + image: + # -- The Kuma DP image repository + repository: "kuma-dp" + # -- Kuma DP ImagePullPolicy + pullPolicy: IfNotPresent + # -- Kuma DP Image Tag. When not specified, the value is copied from global.tag + tag: + + initImage: + # -- The Kuma DP init image repository + repository: "kuma-init" + # -- Kuma DP init image tag When not specified, the value is copied from global.tag + tag: + +ingress: + # -- If true, it deploys Ingress for cross cluster communication + enabled: false + + # -- Labels to add to resources, in addition to default labels + extraLabels: {} + + # -- Time for which old listener will still be active as draining + drainTime: 30s + + # -- Number of replicas of the Ingress. Ignored when autoscaling is enabled. + replicas: 1 + + # -- Log level for ingress (available values: off|info|debug) + logLevel: info + + # -- Define the resources to allocate to mesh ingress + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: 1000m + memory: 512Mi + + # -- Pod lifecycle settings (useful for adding a preStop hook, when + # using AWS ALB or NLB) + lifecycle: {} + + # -- Number of seconds to wait before force killing the pod. Make sure to + # update this if you add a preStop hook. + terminationGracePeriodSeconds: 40 + + # Horizontal Pod Autoscaling configuration + autoscaling: + # -- Whether to enable Horizontal Pod Autoscaling, which requires the [Metrics Server](https://github.com/kubernetes-sigs/metrics-server) in the cluster + enabled: false + + # -- The minimum CP pods to allow + minReplicas: 2 + # -- The max CP pods to scale to + maxReplicas: 5 + + # -- For clusters that don't support autoscaling/v2, autoscaling/v1 is used + targetCPUUtilizationPercentage: 80 + # -- For clusters that do support autoscaling/v2, use metrics + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 80 + + service: + # -- Whether to create a Service resource. + enabled: true + # -- Service type of the Ingress + type: LoadBalancer + # -- (string) Optionally specify IP to be used by cloud provider when configuring load balancer + loadBalancerIP: + # -- Additional annotations to put on the Ingress service + annotations: { } + # -- Port on which Ingress is exposed + port: 10001 + # -- Port on which service is exposed on Node for service of type NodePort + nodePort: + # -- Additional pod annotations (deprecated favor `podAnnotations`) + annotations: { } + # -- Additional pod annotations + podAnnotations: { } + # -- Node Selector for the Ingress pods + nodeSelector: + kubernetes.io/os: linux + # -- Tolerations for the Ingress pods + tolerations: [] + podDisruptionBudget: + # -- Whether to create a pod disruption budget + enabled: false + # -- The maximum number of unavailable pods allowed by the budget + maxUnavailable: 1 + + # -- Affinity placement rule for the Kuma Ingress pods + # This is rendered as a template, so you can reference other helm variables + # or includes. + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + # These match the selector labels used on the deployment. + matchExpressions: + - key: app.kubernetes.io/name + operator: In + values: + - '{{ include "kuma.name" . }}' + - key: app.kubernetes.io/instance + operator: In + values: + - '{{ .Release.Name }}' + - key: app + operator: In + values: + - kuma-ingress + topologyKey: kubernetes.io/hostname + + # -- Topology spread constraints rule for the Kuma Mesh Ingress pods. + # This is rendered as a template, so you can use variables to generate match labels. + topologySpreadConstraints: + + # -- Security context at the pod level for ingress + podSecurityContext: + runAsNonRoot: true + runAsUser: 5678 + runAsGroup: 5678 + + # -- Security context at the container level for ingress + containerSecurityContext: + readOnlyRootFilesystem: true + + # -- Annotations to add for Control Plane's Service Account + serviceAccountAnnotations: { } + # -- Whether to automountServiceAccountToken for cp. Optionally set to false + automountServiceAccountToken: true + +egress: + # -- If true, it deploys Egress for cross cluster communication + enabled: false + # -- Labels to add to resources, in addition to the default labels. + extraLabels: {} + # -- Time for which old listener will still be active as draining + drainTime: 30s + # -- Number of replicas of the Egress. Ignored when autoscaling is enabled. + replicas: 1 + + # -- Log level for egress (available values: off|info|debug) + logLevel: info + + # Horizontal Pod Autoscaling configuration + autoscaling: + # -- Whether to enable Horizontal Pod Autoscaling, which requires the [Metrics Server](https://github.com/kubernetes-sigs/metrics-server) in the cluster + enabled: false + + # -- The minimum CP pods to allow + minReplicas: 2 + # -- The max CP pods to scale to + maxReplicas: 5 + + # -- For clusters that don't support autoscaling/v2, autoscaling/v1 is used + targetCPUUtilizationPercentage: 80 + # -- For clusters that do support autoscaling/v2, use metrics + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 80 + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: 1000m + memory: 512Mi + + service: + # -- Whether to create the service object + enabled: true + # -- Service type of the Egress + type: ClusterIP + # -- (string) Optionally specify IP to be used by cloud provider when configuring load balancer + loadBalancerIP: + # -- Additional annotations to put on the Egress service + annotations: { } + # -- Port on which Egress is exposed + port: 10002 + # -- Port on which service is exposed on Node for service of type NodePort + nodePort: + # -- Additional pod annotations (deprecated favor `podAnnotations`) + annotations: { } + # -- Additional pod annotations + podAnnotations: { } + # -- Node Selector for the Egress pods + nodeSelector: + kubernetes.io/os: linux + # -- Tolerations for the Egress pods + tolerations: [] + podDisruptionBudget: + # -- Whether to create a pod disruption budget + enabled: false + # -- The maximum number of unavailable pods allowed by the budget + maxUnavailable: 1 + + # -- Affinity placement rule for the Kuma Egress pods. + # This is rendered as a template, so you can reference other helm variables or includes. + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + # These match the selector labels used on the deployment. + matchExpressions: + - key: app.kubernetes.io/name + operator: In + values: + - '{{ include "kuma.name" . }}' + - key: app.kubernetes.io/instance + operator: In + values: + - '{{ .Release.Name }}' + - key: app + operator: In + values: + - kuma-egress + topologyKey: kubernetes.io/hostname + + # -- Topology spread constraints rule for the Kuma Egress pods. + # This is rendered as a template, so you can use variables to generate match labels. + topologySpreadConstraints: + + # -- Security context at the pod level for egress + podSecurityContext: + runAsNonRoot: true + runAsUser: 5678 + runAsGroup: 5678 + + # -- Security context at the container level for egress + containerSecurityContext: + readOnlyRootFilesystem: true + + # -- Annotations to add for Control Plane's Service Account + serviceAccountAnnotations: { } + # -- Whether to automountServiceAccountToken for cp. Optionally set to false + automountServiceAccountToken: true + +kumactl: + image: + # -- The kumactl image repository + repository: kumactl + # -- The kumactl image tag. When not specified, the value is copied from global.tag + tag: + +kubectl: + image: + # -- The kubectl image registry + registry: docker.io + # -- The kubectl image repository + repository: bitnami/kubectl + # -- The kubectl image tag + tag: "1.27.5" +hooks: + # -- Node selector for the HELM hooks + nodeSelector: + kubernetes.io/os: linux + # -- Tolerations for the HELM hooks + tolerations: [] + # -- Security context at the pod level for crd/webhook/ns + podSecurityContext: + runAsNonRoot: true + + # -- Security context at the container level for crd/webhook/ns + containerSecurityContext: + readOnlyRootFilesystem: true + + # -- ebpf-cleanup hook needs write access to the root filesystem to clean ebpf programs + # Changing below values will potentially break ebpf cleanup completely, + # so be cautious when doing so. + ebpfCleanup: + # -- Security context at the pod level for crd/webhook/cleanup-ebpf + podSecurityContext: + runAsNonRoot: false + # -- Security context at the container level for crd/webhook/cleanup-ebpf + containerSecurityContext: + readOnlyRootFilesystem: false + +experimental: + # Configuration for the experimental ebpf mode for transparent proxy + ebpf: + # -- If true, ebpf will be used instead of using iptables to install/configure transparent proxy + enabled: false + # -- Name of the environmental variable which will contain the IP address of a pod + instanceIPEnvVarName: INSTANCE_IP + # -- Path where BPF file system should be mounted + bpffsPath: /sys/fs/bpf + # -- Host's cgroup2 path + cgroupPath: /sys/fs/cgroup + # -- Name of the network interface which TC programs should be attached to, we'll try to automatically determine it if empty + tcAttachIface: "" + # -- Path where compiled eBPF programs which will be installed can be found + programsSourcePath: /tmp/kuma-ebpf + # -- If false, it uses legacy API for resource synchronization + deltaKds: true + # -- If true, enable native Kubernetes sidecars. This requires at least + # Kubernetes v1.29 + sidecarContainers: false + +# Postgres' settings for universal control plane on k8s +postgres: + # -- Postgres port, password should be provided as a secret reference in "controlPlane.secrets" + # with the Env value "KUMA_STORE_POSTGRES_PASSWORD". + # Example: + # controlPlane: + # secrets: + # - Secret: postgres-postgresql + # Key: postgresql-password + # Env: KUMA_STORE_POSTGRES_PASSWORD + port: "5432" + # TLS settings + tls: + # -- Mode of TLS connection. Available values are: "disable", "verifyNone", "verifyCa", "verifyFull" + mode: disable # ENV: KUMA_STORE_POSTGRES_TLS_MODE + # -- Whether to disable SNI the postgres `sslsni` option. + disableSSLSNI: false # ENV: KUMA_STORE_POSTGRES_TLS_DISABLE_SSLSNI + # -- Secret name that contains the ca.crt + caSecretName: + # -- Secret name that contains the client tls.crt, tls.key + secretName: + +# @ignored for helm-docs +plugins: + resources: + hostnamegenerators: true + meshexternalservices: true + meshservices: true + policies: + meshaccesslogs: true + meshcircuitbreakers: true + meshfaultinjections: true + meshhealthchecks: true + meshhttproutes: true + meshloadbalancingstrategies: true + meshmetrics: true + meshpassthroughs: true + meshproxypatches: true + meshratelimits: true + meshretries: true + meshtcproutes: true + meshtimeouts: true + meshtraces: true + meshtrafficpermissions: true diff --git a/charts/new-relic/nri-bundle/5.0.91/.helmignore b/charts/new-relic/nri-bundle/5.0.91/.helmignore new file mode 100644 index 0000000000..50af031725 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/.helmignore @@ -0,0 +1,22 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/new-relic/nri-bundle/5.0.91/Chart.lock b/charts/new-relic/nri-bundle/5.0.91/Chart.lock new file mode 100644 index 0000000000..31a101fbc0 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/Chart.lock @@ -0,0 +1,39 @@ +dependencies: +- name: newrelic-infrastructure + repository: https://newrelic.github.io/nri-kubernetes + version: 3.34.4 +- name: nri-prometheus + repository: https://newrelic.github.io/nri-prometheus + version: 2.1.18 +- name: newrelic-prometheus-agent + repository: https://newrelic.github.io/newrelic-prometheus-configurator + version: 1.14.3 +- name: nri-metadata-injection + repository: https://newrelic.github.io/k8s-metadata-injection + version: 4.21.0 +- name: newrelic-k8s-metrics-adapter + repository: https://newrelic.github.io/newrelic-k8s-metrics-adapter + version: 1.11.2 +- name: kube-state-metrics + repository: https://prometheus-community.github.io/helm-charts + version: 5.12.1 +- name: nri-kube-events + repository: https://newrelic.github.io/nri-kube-events + version: 3.10.5 +- name: newrelic-logging + repository: https://newrelic.github.io/helm-charts + version: 1.22.4 +- name: newrelic-pixie + repository: https://newrelic.github.io/helm-charts + version: 2.1.4 +- name: k8s-agents-operator + repository: https://newrelic.github.io/k8s-agents-operator + version: 0.11.0 +- name: pixie-operator-chart + repository: https://pixie-operator-charts.storage.googleapis.com + version: 0.1.6 +- name: newrelic-infra-operator + repository: https://newrelic.github.io/newrelic-infra-operator + version: 2.11.2 +digest: sha256:4301b81fdd0e9f447ec72c9362d7ea2791b9e2df674057b9d47e4bb62e5d697b +generated: "2024-09-02T13:32:30.68661949Z" diff --git a/charts/new-relic/nri-bundle/5.0.91/Chart.yaml b/charts/new-relic/nri-bundle/5.0.91/Chart.yaml new file mode 100644 index 0000000000..10d91f7b67 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/Chart.yaml @@ -0,0 +1,85 @@ +annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: New Relic + catalog.cattle.io/release-name: nri-bundle +apiVersion: v2 +dependencies: +- condition: infrastructure.enabled,newrelic-infrastructure.enabled + name: newrelic-infrastructure + repository: file://./charts/newrelic-infrastructure + version: 3.34.4 +- condition: prometheus.enabled,nri-prometheus.enabled + name: nri-prometheus + repository: file://./charts/nri-prometheus + version: 2.1.18 +- condition: newrelic-prometheus-agent.enabled + name: newrelic-prometheus-agent + repository: file://./charts/newrelic-prometheus-agent + version: 1.14.3 +- condition: webhook.enabled,nri-metadata-injection.enabled + name: nri-metadata-injection + repository: file://./charts/nri-metadata-injection + version: 4.21.0 +- condition: metrics-adapter.enabled,newrelic-k8s-metrics-adapter.enabled + name: newrelic-k8s-metrics-adapter + repository: file://./charts/newrelic-k8s-metrics-adapter + version: 1.11.2 +- condition: ksm.enabled,kube-state-metrics.enabled + name: kube-state-metrics + repository: file://./charts/kube-state-metrics + version: 5.12.1 +- condition: kubeEvents.enabled,nri-kube-events.enabled + name: nri-kube-events + repository: file://./charts/nri-kube-events + version: 3.10.5 +- condition: logging.enabled,newrelic-logging.enabled + name: newrelic-logging + repository: file://./charts/newrelic-logging + version: 1.22.4 +- condition: newrelic-pixie.enabled + name: newrelic-pixie + repository: file://./charts/newrelic-pixie + version: 2.1.4 +- condition: k8s-agents-operator.enabled + name: k8s-agents-operator + repository: file://./charts/k8s-agents-operator + version: 0.11.0 +- alias: pixie-chart + condition: pixie-chart.enabled + name: pixie-operator-chart + repository: file://./charts/pixie-operator-chart + version: 0.1.6 +- condition: newrelic-infra-operator.enabled + name: newrelic-infra-operator + repository: file://./charts/newrelic-infra-operator + version: 2.11.2 +description: Groups together the individual charts for the New Relic Kubernetes solution + for a more comfortable deployment. +home: https://github.com/newrelic/helm-charts +icon: file://assets/icons/nri-bundle.svg +keywords: +- infrastructure +- newrelic +- monitoring +maintainers: +- name: juanjjaramillo + url: https://github.com/juanjjaramillo +- name: csongnr + url: https://github.com/csongnr +- name: dbudziwojskiNR + url: https://github.com/dbudziwojskiNR +name: nri-bundle +sources: +- https://github.com/newrelic/nri-bundle/ +- https://github.com/newrelic/nri-bundle/tree/master/charts/nri-bundle +- https://github.com/newrelic/nri-kubernetes/tree/master/charts/newrelic-infrastructure +- https://github.com/newrelic/nri-prometheus/tree/master/charts/nri-prometheus +- https://github.com/newrelic/newrelic-prometheus-configurator/tree/master/charts/newrelic-prometheus-agent +- https://github.com/newrelic/k8s-metadata-injection/tree/master/charts/nri-metadata-injection +- https://github.com/newrelic/newrelic-k8s-metrics-adapter/tree/master/charts/newrelic-k8s-metrics-adapter +- https://github.com/newrelic/nri-kube-events/tree/master/charts/nri-kube-events +- https://github.com/newrelic/helm-charts/tree/master/charts/newrelic-logging +- https://github.com/newrelic/helm-charts/tree/master/charts/newrelic-pixie +- https://github.com/newrelic/newrelic-infra-operator/tree/master/charts/newrelic-infra-operator +- https://github.com/newrelic/k8s-agents-operator/tree/master/charts/k8s-agents-operator +version: 5.0.91 diff --git a/charts/new-relic/nri-bundle/5.0.91/README.md b/charts/new-relic/nri-bundle/5.0.91/README.md new file mode 100644 index 0000000000..3fcc97d2b7 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/README.md @@ -0,0 +1,200 @@ +# nri-bundle + +Groups together the individual charts for the New Relic Kubernetes solution for a more comfortable deployment. + +**Homepage:** + +## Bundled charts + +This chart does not deploy anything by itself but has many charts as dependencies. This allows you to easily install and upgrade the New Relic +Kubernetes Integration using only one chart. + +In case you need more information about each component this chart installs, or you are an advanced user that want to install each component separately, +here is a list of components that this chart installs and where you can find more information about them: + +| Component | Installed by default? | Description | +|------------------------------|-----------------------|-------------| +| [newrelic-infrastructure](https://github.com/newrelic/nri-kubernetes/tree/main/charts/newrelic-infrastructure) | Yes | Sends metrics about nodes, cluster objects (e.g. Deployments, Pods), and the control plane to New Relic. | +| [nri-metadata-injection](https://github.com/newrelic/k8s-metadata-injection/tree/main/charts/nri-metadata-injection) | Yes | Enriches New Relic-instrumented applications (APM) with Kubernetes information. | +| [kube-state-metrics](https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-state-metrics) | | Required for `newrelic-infrastructure` to gather cluster-level metrics. | +| [nri-kube-events](https://github.com/newrelic/nri-kube-events/tree/main/charts/nri-kube-events) | | Reports Kubernetes events to New Relic. | +| [newrelic-infra-operator](https://github.com/newrelic/newrelic-infra-operator/tree/main/charts/newrelic-infra-operator) | | (Beta) Used with Fargate or serverless environments to inject `newrelic-infrastructure` as a sidecar instead of the usual DaemonSet. | +| [newrelic-k8s-metrics-adapter](https://github.com/newrelic/newrelic-k8s-metrics-adapter/tree/main/charts/newrelic-k8s-metrics-adapter) | | (Beta) Provides a source of data for Horizontal Pod Autoscalers (HPA) based on a NRQL query from New Relic. | +| [newrelic-logging](https://github.com/newrelic/helm-charts/tree/master/charts/newrelic-logging) | | Sends logs for Kubernetes components and workloads running on the cluster to New Relic. | +| [nri-prometheus](https://github.com/newrelic/nri-prometheus/tree/main/charts/nri-prometheus) | | Sends metrics from applications exposing Prometheus metrics to New Relic. | +| [newrelic-prometheus-configurator](https://github.com/newrelic/newrelic-prometheus-configurator/tree/master/charts/newrelic-prometheus-agent) | | Configures instances of Prometheus in Agent mode to send metrics to the New Relic Prometheus endpoint. | +| [newrelic-pixie](https://github.com/newrelic/helm-charts/tree/master/charts/newrelic-pixie) | | Connects to the Pixie API and enables the New Relic plugin in Pixie. The plugin allows you to export data from Pixie to New Relic for long-term data retention. | +| [Pixie](https://docs.pixielabs.ai/installing-pixie/install-schemes/helm/#3.-deploy) | | Is an open source observability tool for Kubernetes applications that uses eBPF to automatically capture telemetry data without the need for manual instrumentation. | +| [k8s-agents-operator](https://github.com/newrelic/k8s-agents-operator/tree/main/charts/k8s-agents-operator) | | (Preview) Streamlines full-stack observability for Kubernetes environments by automating APM instrumentation alongside Kubernetes agent deployment. | + +## Configure components + +It is possible to configure settings for the individual charts this chart groups by specifying values for them under a key using the name of the chart, +as specified in [helm documentation](https://helm.sh/docs/chart_template_guide/subcharts_and_globals). + +For example, by adding the following to the `values.yml` file: + +```yaml +# Configuration settings for the newrelic-infrastructure chart +newrelic-infrastructure: + # Any key defined in the values.yml file for the newrelic-infrastructure chart can be configured here: + # https://github.com/newrelic/nri-kubernetes/blob/main/charts/newrelic-infrastructure/values.yaml + + verboseLog: false + + resources: + limits: + memory: 512M +``` + +It is possible to override any entry of the [`newrelic-infrastructure`](https://github.com/newrelic/nri-kubernetes/tree/main/charts/newrelic-infrastructure) +chart, as defined in their [`values.yml` file](https://github.com/newrelic/nri-kubernetes/blob/main/charts/newrelic-infrastructure/values.yaml). + +The same approach can be followed to update any of the subcharts. + +After making these changes to the `values.yml` file, or a custom values file, make sure to apply them using: + +``` +$ helm upgrade --reuse-values -f values.yaml [RELEASE] newrelic/nri-bundle +``` + +Where `[RELEASE]` is the name of the helm release, e.g. `newrelic-bundle`. + +## Monitor on host integrations + +If you wish to monitor services running on Kubernetes you can provide integrations +configuration under `integrations_config` that it will passed down to the `newrelic-infrastructure` chart. + +You just need to create a new entry where the "name" is the filename of the configuration file and the data is the content of +the integration configuration. The name must end in ".yaml" as this will be the +filename generated and the Infrastructure agent only looks for YAML files. + +The data part is the actual integration configuration as described in the spec here: +https://docs.newrelic.com/docs/integrations/integrations-sdk/file-specifications/integration-configuration-file-specifications-agent-v180 + +In the following example you can see how to monitor a Redis integration with autodiscovery + +```yaml +newrelic-infrastructure: + integrations: + nri-redis-sampleapp: + discovery: + command: + exec: /var/db/newrelic-infra/nri-discovery-kubernetes --tls --port 10250 + match: + label.app: sampleapp + integrations: + - name: nri-redis + env: + # using the discovered IP as the hostname address + HOSTNAME: ${discovery.ip} + PORT: 6379 + labels: + env: test +``` + +## Bring your own KSM + +New Relic Kubernetes Integration requires an instance of kube-state-metrics (KSM) to be running in the cluster, which this chart pulls as a dependency. If you are already running or want to run your own KSM instance, you will need to make some small adjustments as described below. + +### Bring your own KSM + +If you already have one KSM instance running, you can point `nri-kubernetes` to your instance: + +```yaml +kube-state-metrics: + # Disable bundled KSM. + enabled: false +newrelic-infrastructure: + ksm: + config: + # Selector for your pre-installed KSM Service. You may need to adjust this to fit your existing installation. + selector: "app.kubernetes.io/name=kube-state-metrics" + # Alternatively, you can specify a fixed URL where KSM is available. Doing so will bypass autodiscovery. + #staticUrl: http://ksm.ksm.svc.cluster.local:8080/metrics +``` + +### Run KSM alongside a different version + +If you need to run a different instance of KSM in your cluster, you can still run a separate instance for the Kubernetes Integration to work as intended: + +```yaml +kube-state-metrics: + # Enable bundled KSM. + enabled: true + prometheusScrape: false + customLabels: + # Label unique to this KSM instance. + newrelic.com/custom-ksm: "true" +newrelic-infrastructure: + ksm: + config: + # Use label above as a selector. + selector: "newrelic.com/custom-ksm=true" +``` + +For more information on supported KSM version visit the [requirements documentation](https://docs.newrelic.com/docs/kubernetes-pixie/kubernetes-integration/get-started/kubernetes-integration-compatibility-requirements#reqs) + +## Values managed globally + +Some of the subchart implement the [New Relic's common Helm library](https://github.com/newrelic/helm-charts/tree/master/library/common-library) which +means that it honors a wide range of defaults and globals common to most New Relic Helm charts. + +Options that can be defined globally include `affinity`, `nodeSelector`, `tolerations`, `proxy` and others. The full list can be found at +[user's guide of the common library](https://github.com/newrelic/helm-charts/blob/master/library/common-library/README.md). + +At the time of writing this document, all the charts from `nri-bundle` except `newrelic-logging` and `synthetics-minion` implements this library and +honors global options as described below. + +Note, the value table below is automatically generated from `values.yaml` by `helm-docs`. If you need to add new fields or update existing fields, please update the `values.yaml` and then run `helm-docs` to update this value table. + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| global | object | See [`values.yaml`](values.yaml) | change the behaviour globally to all the supported helm charts. See [user's guide of the common library](https://github.com/newrelic/helm-charts/blob/master/library/common-library/README.md) for further information. | +| global.affinity | object | `{}` | Sets pod/node affinities | +| global.cluster | string | `""` | The cluster name for the Kubernetes cluster. | +| global.containerSecurityContext | object | `{}` | Sets security context (at container level) | +| global.customAttributes | object | `{}` | Adds extra attributes to the cluster and all the metrics emitted to the backend | +| global.customSecretLicenseKey | string | `""` | Key in the Secret object where the license key is stored | +| global.customSecretName | string | `""` | Name of the Secret object where the license key is stored | +| global.dnsConfig | object | `{}` | Sets pod's dnsConfig | +| global.fargate | bool | false | Must be set to `true` when deploying in an EKS Fargate environment | +| global.hostNetwork | bool | false | Sets pod's hostNetwork | +| global.images.pullSecrets | list | `[]` | Set secrets to be able to fetch images | +| global.images.registry | string | `""` | Changes the registry where to get the images. Useful when there is an internal image cache/proxy | +| global.insightsKey | string | `""` | The license key for your New Relic Account. This will be preferred configuration option if both `insightsKey` and `customSecret` are specified. | +| global.labels | object | `{}` | Additional labels for chart objects | +| global.licenseKey | string | `""` | The license key for your New Relic Account. This will be preferred configuration option if both `licenseKey` and `customSecret` are specified. | +| global.lowDataMode | bool | false | Reduces number of metrics sent in order to reduce costs | +| global.nodeSelector | object | `{}` | Sets pod's node selector | +| global.nrStaging | bool | false | Send the metrics to the staging backend. Requires a valid staging license key | +| global.podLabels | object | `{}` | Additional labels for chart pods | +| global.podSecurityContext | object | `{}` | Sets security context (at pod level) | +| global.priorityClassName | string | `""` | Sets pod's priorityClassName | +| global.privileged | bool | false | In each integration it has different behavior. See [Further information](#values-managed-globally-3) but all aims to send less metrics to the backend to try to save costs | | +| global.proxy | string | `""` | Configures the integration to send all HTTP/HTTPS request through the proxy in that URL. The URL should have a standard format like `https://user:password@hostname:port` | +| global.serviceAccount.annotations | object | `{}` | Add these annotations to the service account we create | +| global.serviceAccount.create | string | `nil` | Configures if the service account should be created or not | +| global.serviceAccount.name | string | `nil` | Change the name of the service account. This is honored if you disable on this chart the creation of the service account so you can use your own | +| global.tolerations | list | `[]` | Sets pod's tolerations to node taints | +| global.verboseLog | bool | false | Sets the debug logs to this integration or all integrations if it is set globally | +| k8s-agents-operator.enabled | bool | `false` | Install the [`k8s-agents-operator` chart](https://github.com/newrelic/k8s-agents-operator/tree/main/charts/k8s-agents-operator) | +| kube-state-metrics.enabled | bool | `false` | Install the [`kube-state-metrics` chart](https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-state-metrics) from the stable helm charts repository. This is mandatory if `infrastructure.enabled` is set to `true` and the user does not provide its own instance of KSM version >=1.8 and <=2.0. Note, kube-state-metrics v2+ disables labels/annotations metrics by default. You can enable the target labels/annotations metrics to be monitored by using the metricLabelsAllowlist/metricAnnotationsAllowList options described [here](https://github.com/prometheus-community/helm-charts/blob/159cd8e4fb89b8b107dcc100287504bb91bf30e0/charts/kube-state-metrics/values.yaml#L274) in your Kubernetes clusters. | +| newrelic-infra-operator.enabled | bool | `false` | Install the [`newrelic-infra-operator` chart](https://github.com/newrelic/newrelic-infra-operator/tree/main/charts/newrelic-infra-operator) (Beta) | +| newrelic-infrastructure.enabled | bool | `true` | Install the [`newrelic-infrastructure` chart](https://github.com/newrelic/nri-kubernetes/tree/main/charts/newrelic-infrastructure) | +| newrelic-k8s-metrics-adapter.enabled | bool | `false` | Install the [`newrelic-k8s-metrics-adapter.` chart](https://github.com/newrelic/newrelic-k8s-metrics-adapter/tree/main/charts/newrelic-k8s-metrics-adapter) (Beta) | +| newrelic-logging.enabled | bool | `false` | Install the [`newrelic-logging` chart](https://github.com/newrelic/helm-charts/tree/master/charts/newrelic-logging) | +| newrelic-pixie.enabled | bool | `false` | Install the [`newrelic-pixie`](https://github.com/newrelic/helm-charts/tree/master/charts/newrelic-pixie) | +| newrelic-prometheus-agent.enabled | bool | `false` | Install the [`newrelic-prometheus-agent` chart](https://github.com/newrelic/newrelic-prometheus-configurator/tree/main/charts/newrelic-prometheus-agent) | +| nri-kube-events.enabled | bool | `false` | Install the [`nri-kube-events` chart](https://github.com/newrelic/nri-kube-events/tree/main/charts/nri-kube-events) | +| nri-metadata-injection.enabled | bool | `true` | Install the [`nri-metadata-injection` chart](https://github.com/newrelic/k8s-metadata-injection/tree/main/charts/nri-metadata-injection) | +| nri-prometheus.enabled | bool | `false` | Install the [`nri-prometheus` chart](https://github.com/newrelic/nri-prometheus/tree/main/charts/nri-prometheus) | +| pixie-chart.enabled | bool | `false` | Install the [`pixie-chart` chart](https://docs.pixielabs.ai/installing-pixie/install-schemes/helm/#3.-deploy) | + +## Maintainers + +* [juanjjaramillo](https://github.com/juanjjaramillo) +* [csongnr](https://github.com/csongnr) +* [dbudziwojskiNR](https://github.com/dbudziwojskiNR) diff --git a/charts/new-relic/nri-bundle/5.0.91/README.md.gotmpl b/charts/new-relic/nri-bundle/5.0.91/README.md.gotmpl new file mode 100644 index 0000000000..269c4925a2 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/README.md.gotmpl @@ -0,0 +1,166 @@ +{{ template "chart.header" . }} +{{ template "chart.deprecationWarning" . }} + +{{ template "chart.description" . }} + +{{ template "chart.homepageLine" . }} + +## Bundled charts + +This chart does not deploy anything by itself but has many charts as dependencies. This allows you to easily install and upgrade the New Relic +Kubernetes Integration using only one chart. + +In case you need more information about each component this chart installs, or you are an advanced user that want to install each component separately, +here is a list of components that this chart installs and where you can find more information about them: + +| Component | Installed by default? | Description | +|------------------------------|-----------------------|-------------| +| [newrelic-infrastructure](https://github.com/newrelic/nri-kubernetes/tree/main/charts/newrelic-infrastructure) | Yes | Sends metrics about nodes, cluster objects (e.g. Deployments, Pods), and the control plane to New Relic. | +| [nri-metadata-injection](https://github.com/newrelic/k8s-metadata-injection/tree/main/charts/nri-metadata-injection) | Yes | Enriches New Relic-instrumented applications (APM) with Kubernetes information. | +| [kube-state-metrics](https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-state-metrics) | | Required for `newrelic-infrastructure` to gather cluster-level metrics. | +| [nri-kube-events](https://github.com/newrelic/nri-kube-events/tree/main/charts/nri-kube-events) | | Reports Kubernetes events to New Relic. | +| [newrelic-infra-operator](https://github.com/newrelic/newrelic-infra-operator/tree/main/charts/newrelic-infra-operator) | | (Beta) Used with Fargate or serverless environments to inject `newrelic-infrastructure` as a sidecar instead of the usual DaemonSet. | +| [newrelic-k8s-metrics-adapter](https://github.com/newrelic/newrelic-k8s-metrics-adapter/tree/main/charts/newrelic-k8s-metrics-adapter) | | (Beta) Provides a source of data for Horizontal Pod Autoscalers (HPA) based on a NRQL query from New Relic. | +| [newrelic-logging](https://github.com/newrelic/helm-charts/tree/master/charts/newrelic-logging) | | Sends logs for Kubernetes components and workloads running on the cluster to New Relic. | +| [nri-prometheus](https://github.com/newrelic/nri-prometheus/tree/main/charts/nri-prometheus) | | Sends metrics from applications exposing Prometheus metrics to New Relic. | +| [newrelic-prometheus-configurator](https://github.com/newrelic/newrelic-prometheus-configurator/tree/master/charts/newrelic-prometheus-agent) | | Configures instances of Prometheus in Agent mode to send metrics to the New Relic Prometheus endpoint. | +| [newrelic-pixie](https://github.com/newrelic/helm-charts/tree/master/charts/newrelic-pixie) | | Connects to the Pixie API and enables the New Relic plugin in Pixie. The plugin allows you to export data from Pixie to New Relic for long-term data retention. | +| [Pixie](https://docs.pixielabs.ai/installing-pixie/install-schemes/helm/#3.-deploy) | | Is an open source observability tool for Kubernetes applications that uses eBPF to automatically capture telemetry data without the need for manual instrumentation. | +| [k8s-agents-operator](https://github.com/newrelic/k8s-agents-operator/tree/main/charts/k8s-agents-operator) | | (Preview) Streamlines full-stack observability for Kubernetes environments by automating APM instrumentation alongside Kubernetes agent deployment. | + +## Configure components + +It is possible to configure settings for the individual charts this chart groups by specifying values for them under a key using the name of the chart, +as specified in [helm documentation](https://helm.sh/docs/chart_template_guide/subcharts_and_globals). + +For example, by adding the following to the `values.yml` file: + +```yaml +# Configuration settings for the newrelic-infrastructure chart +newrelic-infrastructure: + # Any key defined in the values.yml file for the newrelic-infrastructure chart can be configured here: + # https://github.com/newrelic/nri-kubernetes/blob/main/charts/newrelic-infrastructure/values.yaml + + verboseLog: false + + resources: + limits: + memory: 512M +``` + +It is possible to override any entry of the [`newrelic-infrastructure`](https://github.com/newrelic/nri-kubernetes/tree/main/charts/newrelic-infrastructure) +chart, as defined in their [`values.yml` file](https://github.com/newrelic/nri-kubernetes/blob/main/charts/newrelic-infrastructure/values.yaml). + +The same approach can be followed to update any of the subcharts. + +After making these changes to the `values.yml` file, or a custom values file, make sure to apply them using: + +``` +$ helm upgrade --reuse-values -f values.yaml [RELEASE] newrelic/nri-bundle +``` + +Where `[RELEASE]` is the name of the helm release, e.g. `newrelic-bundle`. + + +## Monitor on host integrations + +If you wish to monitor services running on Kubernetes you can provide integrations +configuration under `integrations_config` that it will passed down to the `newrelic-infrastructure` chart. + +You just need to create a new entry where the "name" is the filename of the configuration file and the data is the content of +the integration configuration. The name must end in ".yaml" as this will be the +filename generated and the Infrastructure agent only looks for YAML files. + +The data part is the actual integration configuration as described in the spec here: +https://docs.newrelic.com/docs/integrations/integrations-sdk/file-specifications/integration-configuration-file-specifications-agent-v180 + +In the following example you can see how to monitor a Redis integration with autodiscovery + +```yaml +newrelic-infrastructure: + integrations: + nri-redis-sampleapp: + discovery: + command: + exec: /var/db/newrelic-infra/nri-discovery-kubernetes --tls --port 10250 + match: + label.app: sampleapp + integrations: + - name: nri-redis + env: + # using the discovered IP as the hostname address + HOSTNAME: ${discovery.ip} + PORT: 6379 + labels: + env: test +``` + +## Bring your own KSM + +New Relic Kubernetes Integration requires an instance of kube-state-metrics (KSM) to be running in the cluster, which this chart pulls as a dependency. If you are already running or want to run your own KSM instance, you will need to make some small adjustments as described below. + +### Bring your own KSM + +If you already have one KSM instance running, you can point `nri-kubernetes` to your instance: + +```yaml +kube-state-metrics: + # Disable bundled KSM. + enabled: false +newrelic-infrastructure: + ksm: + config: + # Selector for your pre-installed KSM Service. You may need to adjust this to fit your existing installation. + selector: "app.kubernetes.io/name=kube-state-metrics" + # Alternatively, you can specify a fixed URL where KSM is available. Doing so will bypass autodiscovery. + #staticUrl: http://ksm.ksm.svc.cluster.local:8080/metrics +``` + +### Run KSM alongside a different version + +If you need to run a different instance of KSM in your cluster, you can still run a separate instance for the Kubernetes Integration to work as intended: + +```yaml +kube-state-metrics: + # Enable bundled KSM. + enabled: true + prometheusScrape: false + customLabels: + # Label unique to this KSM instance. + newrelic.com/custom-ksm: "true" +newrelic-infrastructure: + ksm: + config: + # Use label above as a selector. + selector: "newrelic.com/custom-ksm=true" +``` + +For more information on supported KSM version visit the [requirements documentation](https://docs.newrelic.com/docs/kubernetes-pixie/kubernetes-integration/get-started/kubernetes-integration-compatibility-requirements#reqs) + +## Values managed globally + +Some of the subchart implement the [New Relic's common Helm library](https://github.com/newrelic/helm-charts/tree/master/library/common-library) which +means that it honors a wide range of defaults and globals common to most New Relic Helm charts. + +Options that can be defined globally include `affinity`, `nodeSelector`, `tolerations`, `proxy` and others. The full list can be found at +[user's guide of the common library](https://github.com/newrelic/helm-charts/blob/master/library/common-library/README.md). + +At the time of writing this document, all the charts from `nri-bundle` except `newrelic-logging` and `synthetics-minion` implements this library and +honors global options as described below. + +Note, the value table below is automatically generated from `values.yaml` by `helm-docs`. If you need to add new fields or update existing fields, please update the `values.yaml` and then run `helm-docs` to update this value table. + +{{ template "chart.valuesSection" . }} + +{{ if .Maintainers }} +## Maintainers +{{ range .Maintainers }} +{{- if .Name }} +{{- if .Url }} +* [{{ .Name }}]({{ .Url }}) +{{- else }} +* {{ .Name }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/app-readme.md b/charts/new-relic/nri-bundle/5.0.91/app-readme.md new file mode 100644 index 0000000000..61e5507873 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/app-readme.md @@ -0,0 +1,5 @@ +# New Relic Kubernetes Integration + +New Relic's Kubernetes integration gives you full observability into the health and performance of your environment, no matter whether you run Kubernetes on-premises or in the cloud. With our [cluster explorer](https://docs.newrelic.com/docs/integrations/kubernetes-integration/cluster-explorer/kubernetes-cluster-explorer), you can cut through layers of complexity to see how your cluster is performing, from the heights of the control plane down to applications running on a single pod. + +You can see the power of the Kubernetes integration in the [cluster explorer](https://docs.newrelic.com/docs/integrations/kubernetes-integration/cluster-explorer/kubernetes-cluster-explorer), where the full picture of a cluster is made available on a single screen: nodes and pods are visualized according to their health and performance, with pending and alerting nodes in the innermost circles. [Predefined alert conditions](https://docs.newrelic.com/docs/integrations/kubernetes-integration/kubernetes-events/kubernetes-integration-predefined-alert-policy) help you troubleshoot issues right from the start. Clicking each node reveals its status and how each app is performing. \ No newline at end of file diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/.helmignore b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/.helmignore new file mode 100644 index 0000000000..0e8a0eb36f --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/.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 +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/Chart.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/Chart.yaml new file mode 100644 index 0000000000..332b4f7563 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/Chart.yaml @@ -0,0 +1,16 @@ +apiVersion: v2 +appVersion: 0.11.0 +description: A Helm chart for the Kubernetes Agents Operator +home: https://github.com/newrelic/k8s-agents-operator/blob/main/charts/k8s-agents-operator/README.md +maintainers: +- name: juanjjaramillo + url: https://github.com/juanjjaramillo +- name: csongnr + url: https://github.com/csongnr +- name: dbudziwojskiNR + url: https://github.com/dbudziwojskiNR +name: k8s-agents-operator +sources: +- https://github.com/newrelic/k8s-agents-operator +type: application +version: 0.11.0 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/README.md b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/README.md new file mode 100644 index 0000000000..75ae8df66c --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/README.md @@ -0,0 +1,191 @@ +# k8s-agents-operator + +![Version: 0.11.0](https://img.shields.io/badge/Version-0.11.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.11.0](https://img.shields.io/badge/AppVersion-0.11.0-informational?style=flat-square) + +A Helm chart for the Kubernetes Agents Operator + +**Homepage:** + +## Prerequisites + +[Helm](https://helm.sh) must be installed to use the charts. Please refer to Helm's [documentation](https://helm.sh/docs) to get started. + +## Installation + +### Requirements + +Add the `jetstack` and `k8s-agents-operator` Helm chart repositories: +```shell +helm repo add jetstack https://charts.jetstack.io +helm repo add k8s-agents-operator https://newrelic.github.io/k8s-agents-operator +``` + +Install the [`cert-manager`](https://github.com/cert-manager/cert-manager) Helm chart: +```shell +helm install cert-manager jetstack/cert-manager \ + --namespace cert-manager \ + --create-namespace \ + --set crds.enabled=true +``` + +### Instrumentation + +Install the [`k8s-agents-operator`](https://github.com/newrelic/k8s-agents-operator) Helm chart: +```shell +helm upgrade --install k8s-agents-operator k8s-agents-operator/k8s-agents-operator \ + --namespace k8s-agents-operator \ + --create-namespace \ + --values your-custom-values.yaml +``` + +### Monitored namespaces + +For each namespace you want the operator to be instrumented, create a secret containing a valid New Relic ingest license key: +```shell +kubectl create secret generic newrelic-key-secret \ + --namespace my-monitored-namespace \ + --from-literal=new_relic_license_key= +``` + +Similarly, for each namespace you need to instrument create the `Instrumentation` custom resource, specifying which APM agents you want to instrument. All available APM agent docker images and corresponding tags are listed on DockerHub: +* [Java](https://hub.docker.com/repository/docker/newrelic/newrelic-java-init/general) +* [Node](https://hub.docker.com/repository/docker/newrelic/newrelic-node-init/general) +* [Python](https://hub.docker.com/repository/docker/newrelic/newrelic-python-init/general) +* [.NET](https://hub.docker.com/repository/docker/newrelic/newrelic-dotnet-init/general) +* [Ruby](https://hub.docker.com/repository/docker/newrelic/newrelic-ruby-init/general) + +```yaml +apiVersion: newrelic.com/v1alpha1 +kind: Instrumentation +metadata: + labels: + app.kubernetes.io/name: instrumentation + app.kubernetes.io/created-by: k8s-agents-operator + name: newrelic-instrumentation +spec: + java: + image: newrelic/newrelic-java-init:latest + # env: + # Example New Relic agent supported environment variables + # - name: NEW_RELIC_LABELS + # value: "environment:auto-injection" + # Example overriding the appName configuration + # - name: NEW_RELIC_POD_NAME + # valueFrom: + # fieldRef: + # fieldPath: metadata.name + # - name: NEW_RELIC_APP_NAME + # value: "$(NEW_RELIC_LABELS)-$(NEW_RELIC_POD_NAME)" + nodejs: + image: newrelic/newrelic-node-init:latest + python: + image: newrelic/newrelic-python-init:latest + dotnet: + image: newrelic/newrelic-dotnet-init:latest + ruby: + image: newrelic/newrelic-ruby-init:latest +``` +In the example above, we show how you can configure the agent settings globally using environment variables. See each agent's configuration documentation for available configuration options: +* [Java](https://docs.newrelic.com/docs/apm/agents/java-agent/configuration/java-agent-configuration-config-file/) +* [Node](https://docs.newrelic.com/docs/apm/agents/nodejs-agent/installation-configuration/nodejs-agent-configuration/) +* [Python](https://docs.newrelic.com/docs/apm/agents/python-agent/configuration/python-agent-configuration/) +* [.NET](https://docs.newrelic.com/docs/apm/agents/net-agent/configuration/net-agent-configuration/) +* [Ruby](https://docs.newrelic.com/docs/apm/agents/ruby-agent/configuration/ruby-agent-configuration/) + +Global agent settings can be overridden in your deployment manifest if a different configuration is required. + +### Annotations + +The `k8s-agents-operator` looks for language-specific annotations when your pods are being scheduled to know which applications you want to monitor. + +Below are the currently supported annotations: +```yaml +instrumentation.newrelic.com/inject-java: "true" +instrumentation.newrelic.com/inject-nodejs: "true" +instrumentation.newrelic.com/inject-python: "true" +instrumentation.newrelic.com/inject-dotnet: "true" +instrumentation.newrelic.com/inject-ruby: "true" +``` + +Example deployment with annotation to instrument the Java agent: +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: spring-petclinic +spec: + selector: + matchLabels: + app: spring-petclinic + replicas: 1 + template: + metadata: + labels: + app: spring-petclinic + annotations: + instrumentation.newrelic.com/inject-java: "true" + spec: + containers: + - name: spring-petclinic + image: ghcr.io/pavolloffay/spring-petclinic:latest + ports: + - containerPort: 8080 + env: + - name: NEW_RELIC_APP_NAME + value: spring-petclinic-demo +``` + +## Available Chart Releases + +To see the available charts: +```shell +helm search repo k8s-agents-operator +``` + +If you want to see a list of all available charts and releases, check [index.yaml](https://newrelic.github.io/k8s-agents-operator/index.yaml). + +## Source Code + +* + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| admissionWebhooks | object | `{"create":true}` | Admission webhooks make sure only requests with correctly formatted rules will get into the Operator | +| controllerManager.kubeRbacProxy.image.repository | string | `"gcr.io/kubebuilder/kube-rbac-proxy"` | | +| controllerManager.kubeRbacProxy.image.tag | string | `"v0.14.0"` | | +| controllerManager.kubeRbacProxy.resources.limits.cpu | string | `"500m"` | | +| controllerManager.kubeRbacProxy.resources.limits.memory | string | `"128Mi"` | | +| controllerManager.kubeRbacProxy.resources.requests.cpu | string | `"5m"` | | +| controllerManager.kubeRbacProxy.resources.requests.memory | string | `"64Mi"` | | +| controllerManager.manager.image.pullPolicy | string | `nil` | | +| controllerManager.manager.image.repository | string | `"newrelic/k8s-agents-operator"` | | +| controllerManager.manager.image.tag | string | `nil` | | +| controllerManager.manager.leaderElection | object | `{"enabled":true}` | Enable leader election mechanism for protecting against split brain if multiple operator pods/replicas are started | +| controllerManager.manager.resources.requests.cpu | string | `"100m"` | | +| controllerManager.manager.resources.requests.memory | string | `"64Mi"` | | +| controllerManager.manager.serviceAccount.create | bool | `true` | | +| controllerManager.replicas | int | `1` | | +| kubernetesClusterDomain | string | `"cluster.local"` | | +| metricsService.ports[0].name | string | `"https"` | | +| metricsService.ports[0].port | int | `8443` | | +| metricsService.ports[0].protocol | string | `"TCP"` | | +| metricsService.ports[0].targetPort | string | `"https"` | | +| metricsService.type | string | `"ClusterIP"` | | +| securityContext | object | `{"fsGroup":65532,"runAsGroup":65532,"runAsNonRoot":true,"runAsUser":65532}` | SecurityContext holds pod-level security attributes and common container settings | +| webhookService.ports[0].port | int | `443` | | +| webhookService.ports[0].protocol | string | `"TCP"` | | +| webhookService.ports[0].targetPort | int | `9443` | | +| webhookService.type | string | `"ClusterIP"` | | + +## Maintainers + +| Name | Email | Url | +| ---- | ------ | --- | +| juanjjaramillo | | | +| csongnr | | | +| dbudziwojskiNR | | | + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.13.1](https://github.com/norwoodj/helm-docs/releases/v1.13.1) diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/README.md.gotmpl b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/README.md.gotmpl new file mode 100644 index 0000000000..fadd3b971c --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/README.md.gotmpl @@ -0,0 +1,157 @@ +{{ template "chart.header" . }} + +{{ template "chart.deprecationWarning" . }} + +{{ template "chart.badgesSection" . }} + +{{ template "chart.description" . }} + +{{ template "chart.homepageLine" . }} + +## Prerequisites + +[Helm](https://helm.sh) must be installed to use the charts. Please refer to Helm's [documentation](https://helm.sh/docs) to get started. + +## Installation + +### Requirements + +Add the `jetstack` and `k8s-agents-operator` Helm chart repositories: +```shell +helm repo add jetstack https://charts.jetstack.io +helm repo add k8s-agents-operator https://newrelic.github.io/k8s-agents-operator +``` + +Install the [`cert-manager`](https://github.com/cert-manager/cert-manager) Helm chart: +```shell +helm install cert-manager jetstack/cert-manager \ + --namespace cert-manager \ + --create-namespace \ + --set crds.enabled=true +``` + +### Instrumentation + +Install the [`k8s-agents-operator`](https://github.com/newrelic/k8s-agents-operator) Helm chart: +```shell +helm upgrade --install k8s-agents-operator k8s-agents-operator/k8s-agents-operator \ + --namespace k8s-agents-operator \ + --create-namespace \ + --values your-custom-values.yaml +``` + +### Monitored namespaces + +For each namespace you want the operator to be instrumented, create a secret containing a valid New Relic ingest license key: +```shell +kubectl create secret generic newrelic-key-secret \ + --namespace my-monitored-namespace \ + --from-literal=new_relic_license_key= +``` + +Similarly, for each namespace you need to instrument create the `Instrumentation` custom resource, specifying which APM agents you want to instrument. All available APM agent docker images and corresponding tags are listed on DockerHub: +* [Java](https://hub.docker.com/repository/docker/newrelic/newrelic-java-init/general) +* [Node](https://hub.docker.com/repository/docker/newrelic/newrelic-node-init/general) +* [Python](https://hub.docker.com/repository/docker/newrelic/newrelic-python-init/general) +* [.NET](https://hub.docker.com/repository/docker/newrelic/newrelic-dotnet-init/general) +* [Ruby](https://hub.docker.com/repository/docker/newrelic/newrelic-ruby-init/general) + +```yaml +apiVersion: newrelic.com/v1alpha1 +kind: Instrumentation +metadata: + labels: + app.kubernetes.io/name: instrumentation + app.kubernetes.io/created-by: k8s-agents-operator + name: newrelic-instrumentation +spec: + java: + image: newrelic/newrelic-java-init:latest + # env: + # Example New Relic agent supported environment variables + # - name: NEW_RELIC_LABELS + # value: "environment:auto-injection" + # Example overriding the appName configuration + # - name: NEW_RELIC_POD_NAME + # valueFrom: + # fieldRef: + # fieldPath: metadata.name + # - name: NEW_RELIC_APP_NAME + # value: "$(NEW_RELIC_LABELS)-$(NEW_RELIC_POD_NAME)" + nodejs: + image: newrelic/newrelic-node-init:latest + python: + image: newrelic/newrelic-python-init:latest + dotnet: + image: newrelic/newrelic-dotnet-init:latest + ruby: + image: newrelic/newrelic-ruby-init:latest +``` +In the example above, we show how you can configure the agent settings globally using environment variables. See each agent's configuration documentation for available configuration options: +* [Java](https://docs.newrelic.com/docs/apm/agents/java-agent/configuration/java-agent-configuration-config-file/) +* [Node](https://docs.newrelic.com/docs/apm/agents/nodejs-agent/installation-configuration/nodejs-agent-configuration/) +* [Python](https://docs.newrelic.com/docs/apm/agents/python-agent/configuration/python-agent-configuration/) +* [.NET](https://docs.newrelic.com/docs/apm/agents/net-agent/configuration/net-agent-configuration/) +* [Ruby](https://docs.newrelic.com/docs/apm/agents/ruby-agent/configuration/ruby-agent-configuration/) + +Global agent settings can be overridden in your deployment manifest if a different configuration is required. + +### Annotations + +The `k8s-agents-operator` looks for language-specific annotations when your pods are being scheduled to know which applications you want to monitor. + +Below are the currently supported annotations: +```yaml +instrumentation.newrelic.com/inject-java: "true" +instrumentation.newrelic.com/inject-nodejs: "true" +instrumentation.newrelic.com/inject-python: "true" +instrumentation.newrelic.com/inject-dotnet: "true" +instrumentation.newrelic.com/inject-ruby: "true" +``` + +Example deployment with annotation to instrument the Java agent: +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: spring-petclinic +spec: + selector: + matchLabels: + app: spring-petclinic + replicas: 1 + template: + metadata: + labels: + app: spring-petclinic + annotations: + instrumentation.newrelic.com/inject-java: "true" + spec: + containers: + - name: spring-petclinic + image: ghcr.io/pavolloffay/spring-petclinic:latest + ports: + - containerPort: 8080 + env: + - name: NEW_RELIC_APP_NAME + value: spring-petclinic-demo +``` + +## Available Chart Releases + +To see the available charts: +```shell +helm search repo k8s-agents-operator +``` + +If you want to see a list of all available charts and releases, check [index.yaml](https://newrelic.github.io/k8s-agents-operator/index.yaml). + +{{ template "chart.sourcesSection" . }} + +{{ template "chart.requirementsSection" . }} + +{{ template "chart.valuesSection" . }} + +{{ template "chart.maintainersSection" . }} + +{{ template "helm-docs.versionFooter" . }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/NOTES.txt b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/NOTES.txt new file mode 100644 index 0000000000..e3fb917640 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/NOTES.txt @@ -0,0 +1,36 @@ +This project is currently in experimental phases and is provided AS-IS WITHOUT WARRANTY OR DEDICATED SUPPORT. +Issues and contributions should be reported to the project's GitHub. +{{- if (include "k8s-agents-operator.areValuesValid" .) }} +===================================== + + ******** + **************** + ********** **********, + &&&**** ****/((( + &&&&&&& (((((( + &&&&&&&&&& (((((( + &&&&&&&& (((((( + &&&&& (((((( + &&&&& (((((((( + &&&&& .(((((((((( + &&&&&(((((((( + &&&(((, + +Your deployment of the New Relic Agent Operator is complete. +You can check on the progress of this by running the following command: + +kubectl get deployments -o wide -w --namespace {{ .Release.Namespace }} {{ template "k8s-agents-operator.fullname" . }} + +WARNING: This deployment will be incomplete until you configure your Instrumentation custom resource definition. +===================================== + +Please visit https://github.com/newrelic/k8s-agents-operator for instructions on how to create & configure the +Instrumentation custom resource definition required by the Operator. +{{- else }} + +############################################################################## +#### ERROR: You did not set a license key. #### +############################################################################## + +This deployment will be incomplete until you get your ingest license key from New Relic. +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/_helpers.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/_helpers.tpl new file mode 100644 index 0000000000..43b57a4d4e --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/_helpers.tpl @@ -0,0 +1,80 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "k8s-agents-operator.name" -}} +{{- .Chart.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 "k8s-agents-operator.fullname" -}} +{{- printf "%s-%s" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "k8s-agents-operator.chart" -}} +{{- printf "%s" .Chart.Name | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "k8s-agents-operator.labels" -}} +helm.sh/chart: {{ include "k8s-agents-operator.chart" . }} +{{ include "k8s-agents-operator.selectorLabels" . }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "k8s-agents-operator.selectorLabels" -}} +app.kubernetes.io/name: {{ include "k8s-agents-operator.chart" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "k8s-agents-operator.serviceAccountName" -}} +{{- if .Values.controllerManager.manager.serviceAccount.create }} +{{- default (include "k8s-agents-operator.name" .) .Values.controllerManager.manager.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.controllerManager.manager.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +Return the licenseKey +*/}} +{{- define "k8s-agents-operator.licenseKey" -}} +{{- if .Values.global}} + {{- if .Values.global.licenseKey }} + {{- .Values.global.licenseKey -}} + {{- else -}} + {{- .Values.licenseKey | default "" -}} + {{- end -}} +{{- else -}} + {{- .Values.licenseKey | default "" -}} +{{- end -}} +{{- end -}} + +{{/* +Returns if the template should render, it checks if the required values are set. +*/}} +{{- define "k8s-agents-operator.areValuesValid" -}} +{{- $licenseKey := include "k8s-agents-operator.licenseKey" . -}} +{{- and (or $licenseKey)}} +{{- end -}} + +{{/* +Controller manager service certificate's secret. +*/}} +{{- define "k8s-agents-operator.certificateSecret" -}} +{{- printf "%s-controller-manager-service-cert" (include "k8s-agents-operator.fullname" .) | trunc 63 | trimSuffix "-" -}} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/certmanager.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/certmanager.yaml new file mode 100644 index 0000000000..54509f673c --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/certmanager.yaml @@ -0,0 +1,17 @@ +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: {{ template "k8s-agents-operator.fullname" . }}-serving-cert + labels: + {{- include "k8s-agents-operator.labels" . | nindent 4 }} +spec: + dnsNames: + - '{{ template "k8s-agents-operator.fullname" . }}-webhook-service.{{ .Release.Namespace }}.svc' + - '{{ template "k8s-agents-operator.fullname" . }}-webhook-service.{{ .Release.Namespace }}.svc.{{ .Values.kubernetesClusterDomain }}' + issuerRef: + kind: Issuer + name: '{{ template "k8s-agents-operator.fullname" . }}-selfsigned-issuer' + secretName: {{ template "k8s-agents-operator.certificateSecret" . }} + subject: + organizationalUnits: + - k8s-agents-operator \ No newline at end of file diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/deployment.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/deployment.yaml new file mode 100644 index 0000000000..29d3b01ec7 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/deployment.yaml @@ -0,0 +1,89 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "k8s-agents-operator.serviceAccountName" . }} + labels: + {{- include "k8s-agents-operator.labels" . | nindent 4 }} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "k8s-agents-operator.fullname" . }} + labels: + control-plane: controller-manager + {{- include "k8s-agents-operator.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.controllerManager.replicas }} + selector: + matchLabels: + control-plane: controller-manager + {{- include "k8s-agents-operator.labels" . | nindent 6 }} + template: + metadata: + labels: + control-plane: controller-manager + {{- include "k8s-agents-operator.labels" . | nindent 8 }} + spec: + containers: + - args: + - --metrics-addr=127.0.0.1:8080 + {{- if .Values.controllerManager.manager.leaderElection.enabled }} + - --enable-leader-election + {{- end }} + - --zap-log-level=info + - --zap-time-encoding=rfc3339nano + env: + - name: KUBERNETES_CLUSTER_DOMAIN + value: {{ quote .Values.kubernetesClusterDomain }} + - name: ENABLE_WEBHOOKS + value: "true" + image: {{ .Values.controllerManager.manager.image.repository }}:{{ .Values.controllerManager.manager.image.tag | default .Chart.AppVersion }} + imagePullPolicy: {{ .Values.controllerManager.manager.image.pullPolicy | default "Always" }} + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: {{- toYaml .Values.controllerManager.manager.resources | nindent 10 }} + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true + - args: + - --secure-listen-address=0.0.0.0:8443 + - --upstream=http://127.0.0.1:8080/ + - --logtostderr=true + - --v=0 + env: + - name: KUBERNETES_CLUSTER_DOMAIN + value: {{ quote .Values.kubernetesClusterDomain }} + image: {{ .Values.controllerManager.kubeRbacProxy.image.repository }}:{{ .Values.controllerManager.kubeRbacProxy.image.tag | default .Chart.AppVersion }} + name: kube-rbac-proxy + ports: + - containerPort: 8443 + name: https + protocol: TCP + resources: {{- toYaml .Values.controllerManager.kubeRbacProxy.resources | nindent 10 }} + serviceAccountName: {{ template "k8s-agents-operator.serviceAccountName" . }} + terminationGracePeriodSeconds: 10 + {{- if or .Values.admissionWebhooks.create .Values.admissionWebhooks.secretName }} + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: {{ template "k8s-agents-operator.certificateSecret" . }} + {{- end }} + securityContext: +{{ toYaml .Values.securityContext | indent 8 }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/instrumentation-crd.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/instrumentation-crd.yaml new file mode 100644 index 0000000000..ae81414fb6 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/instrumentation-crd.yaml @@ -0,0 +1,1150 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: instrumentations.newrelic.com + annotations: + controller-gen.kubebuilder.io/version: v0.11.3 + labels: + {{- include "k8s-agents-operator.labels" . | nindent 4 }} +spec: + group: newrelic.com + names: + kind: Instrumentation + listKind: InstrumentationList + plural: instrumentations + shortNames: + - nragent + - nragents + singular: instrumentation + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: Instrumentation is the Schema for the instrumentations 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: InstrumentationSpec defines the desired state of Instrumentation + properties: + dotnet: + description: DotNet defines configuration for dotnet auto-instrumentation. + properties: + env: + description: Env defines DotNet specific env vars. If the former + var had been defined, then the other vars would be ignored. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. If + a variable cannot be resolved, the reference in the input + string will be unchanged. Double $$ are reduced to a single + $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless + of whether the variable exists or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.podIP, + status.podIPs.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.ephemeral-storage) are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + image: + description: Image is a container image with DotNet agent and + auto-instrumentation. + type: string + type: object + env: + description: 'Env defines common env vars. There are four layers for + env vars'' definitions and the precedence order is: `original container + env vars` > `language specific env vars` > `common env vars` > `instrument + spec configs'' vars`. If the former var had been defined, then the + other vars would be ignored.' + items: + description: EnvVar represents an environment variable present in + a Container. + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are expanded using + the previously defined environment variables in the container + and any service environment variables. If a variable cannot + be resolved, the reference in the input string will be unchanged. + Double $$ are reduced to a single $, which allows for escaping + the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the + string literal "$(VAR_NAME)". Escaped references will never + be expanded, regardless of whether the variable exists or + not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's value. Cannot + be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, + status.podIP, status.podIPs.' + properties: + apiVersion: + description: Version of the schema the FieldPath is + written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified + API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.ephemeral-storage) are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the exposed + resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + exporter: + description: Exporter defines exporter configuration. + properties: + endpoint: + description: Endpoint is address of the collector with OTLP endpoint. + type: string + type: object + go: + description: Go defines configuration for Go auto-instrumentation. + When using Go auto-instrumentation you must provide a value for + the OTEL_GO_AUTO_TARGET_EXE env var via the Instrumentation env + vars or via the instrumentation.opentelemetry.io/otel-go-auto-target-exe + pod annotation. Failure to set this value causes instrumentation + injection to abort, leaving the original pod unchanged. + properties: + env: + description: 'Env defines Go specific env vars. There are four + layers for env vars'' definitions and the precedence order is: + `original container env vars` > `language specific env vars` + > `common env vars` > `instrument spec configs'' vars`. If the + former var had been defined, then the other vars would be ignored.' + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. If + a variable cannot be resolved, the reference in the input + string will be unchanged. Double $$ are reduced to a single + $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless + of whether the variable exists or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.podIP, + status.podIPs.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.ephemeral-storage) are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + image: + description: Image is a container image with Go SDK and auto-instrumentation. + type: string + resourceRequirements: + description: Resources describes the compute resource requirements. + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate. \n This field is immutable. It can only be + set for containers." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in + pod.spec.resourceClaims of the Pod where this field + is used. It makes that resource available inside a + container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + 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 + volumeLimitSize: + anyOf: + - type: integer + - type: string + description: VolumeSizeLimit defines size limit for volume used + for auto-instrumentation. The default size is 200Mi. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + java: + description: Java defines configuration for java auto-instrumentation. + properties: + env: + description: Env defines java specific env vars. If the former + var had been defined, then the other vars would be ignored. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. If + a variable cannot be resolved, the reference in the input + string will be unchanged. Double $$ are reduced to a single + $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless + of whether the variable exists or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.podIP, + status.podIPs.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.ephemeral-storage) are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + image: + description: Image is a container image with javaagent auto-instrumentation + JAR. + type: string + type: object + nodejs: + description: NodeJS defines configuration for nodejs auto-instrumentation. + properties: + env: + description: Env defines nodejs specific env vars. If the former + var had been defined, then the other vars would be ignored. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. If + a variable cannot be resolved, the reference in the input + string will be unchanged. Double $$ are reduced to a single + $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless + of whether the variable exists or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.podIP, + status.podIPs.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.ephemeral-storage) are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + image: + description: Image is a container image with NodeJS agent and + auto-instrumentation. + type: string + type: object + php: + description: Php defines configuration for php auto-instrumentation. + properties: + env: + description: Env defines Php specific env vars. If the former + var had been defined, then the other vars would be ignored. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. If + a variable cannot be resolved, the reference in the input + string will be unchanged. Double $$ are reduced to a single + $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless + of whether the variable exists or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.podIP, + status.podIPs.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.ephemeral-storage) are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + image: + description: Image is a container image with Php agent and auto-instrumentation. + type: string + type: object + propagators: + description: Propagators defines inter-process context propagation + configuration. Values in this list will be set in the OTEL_PROPAGATORS + env var. Enum=tracecontext;none + items: + description: Propagator represents the propagation type. + enum: + - tracecontext + - none + type: string + type: array + python: + description: Python defines configuration for python auto-instrumentation. + properties: + env: + description: Env defines python specific env vars. If the former + var had been defined, then the other vars would be ignored. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. If + a variable cannot be resolved, the reference in the input + string will be unchanged. Double $$ are reduced to a single + $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless + of whether the variable exists or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.podIP, + status.podIPs.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.ephemeral-storage) are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + image: + description: Image is a container image with Python agent and + auto-instrumentation. + type: string + type: object + ruby: + description: Ruby defines configuration for ruby auto-instrumentation. + properties: + env: + description: Env defines Ruby specific env vars. If the former + var had been defined, then the other vars would be ignored. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. If + a variable cannot be resolved, the reference in the input + string will be unchanged. Double $$ are reduced to a single + $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless + of whether the variable exists or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.podIP, + status.podIPs.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.ephemeral-storage) are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + image: + description: Image is a container image with Ruby agent and + auto-instrumentation. + type: string + type: object + resource: + description: Resource defines the configuration for the resource attributes, + as defined by the OpenTelemetry specification. + properties: + addK8sUIDAttributes: + description: AddK8sUIDAttributes defines whether K8s UID attributes + should be collected (e.g. k8s.deployment.uid). + type: boolean + resourceAttributes: + additionalProperties: + type: string + description: 'Attributes defines attributes that are added to + the resource. For example environment: dev' + type: object + type: object + sampler: + description: Sampler defines sampling configuration. + properties: + argument: + description: Argument defines sampler argument. The value depends + on the sampler type. For instance for parentbased_traceidratio + sampler type it is a number in range [0..1] e.g. 0.25. The value + will be set in the OTEL_TRACES_SAMPLER_ARG env var. + type: string + type: + description: Type defines sampler type. The value will be set + in the OTEL_TRACES_SAMPLER env var. The value can be for instance + parentbased_always_on, parentbased_always_off, parentbased_traceidratio... + enum: + - always_on + - always_off + - traceidratio + - parentbased_always_on + - parentbased_always_off + - parentbased_traceidratio + type: string + type: object + type: object + status: + description: InstrumentationStatus defines the observed state of Instrumentation + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null \ No newline at end of file diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/leader-election-rbac.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/leader-election-rbac.yaml new file mode 100644 index 0000000000..57a5be3a3e --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/leader-election-rbac.yaml @@ -0,0 +1,49 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "k8s-agents-operator.fullname" . }}-leader-election-role + labels: + {{- include "k8s-agents-operator.labels" . | nindent 4 }} +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "k8s-agents-operator.fullname" . }}-leader-election-rolebinding + labels: + {{- include "k8s-agents-operator.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: '{{ template "k8s-agents-operator.fullname" . }}-leader-election-role' +subjects: +- kind: ServiceAccount + name: '{{ template "k8s-agents-operator.serviceAccountName" . }}' + namespace: '{{ .Release.Namespace }}' \ No newline at end of file diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/manager-rbac.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/manager-rbac.yaml new file mode 100644 index 0000000000..7a1d9d3bf4 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/manager-rbac.yaml @@ -0,0 +1,76 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "k8s-agents-operator.fullname" . }}-manager-role + labels: + {{- include "k8s-agents-operator.labels" . | nindent 4 }} +rules: +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - "" + resources: + - namespaces + verbs: + - list + - watch +- apiGroups: + - apps + resources: + - replicasets + verbs: + - get + - list + - watch +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - get + - list + - update +- apiGroups: + - newrelic.com + resources: + - instrumentations + verbs: + - get + - list + - patch + - update + - watch +- apiGroups: + - route.openshift.io + resources: + - routes + - routes/custom-host + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "k8s-agents-operator.fullname" . }}-manager-rolebinding + labels: + {{- include "k8s-agents-operator.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: '{{ template "k8s-agents-operator.fullname" . }}-manager-role' +subjects: +- kind: ServiceAccount + name: '{{ template "k8s-agents-operator.serviceAccountName" . }}' + namespace: '{{ .Release.Namespace }}' \ No newline at end of file diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/mutating-webhook-configuration.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/mutating-webhook-configuration.yaml new file mode 100644 index 0000000000..f37ad6a79a --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/mutating-webhook-configuration.yaml @@ -0,0 +1,49 @@ +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + name: {{ template "k8s-agents-operator.fullname" . }}-mutating-webhook-configuration + annotations: + cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ template "k8s-agents-operator.fullname" . }}-serving-cert + labels: + {{- include "k8s-agents-operator.labels" . | nindent 4 }} +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: '{{ template "k8s-agents-operator.fullname" . }}-webhook-service' + namespace: '{{ .Release.Namespace }}' + path: /mutate-newrelic-com-v1alpha1-instrumentation + failurePolicy: Fail + name: instrumentation.kb.io + rules: + - apiGroups: + - newrelic.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - instrumentations + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: '{{ template "k8s-agents-operator.fullname" . }}-webhook-service' + namespace: '{{ .Release.Namespace }}' + path: /mutate-v1-pod + failurePolicy: Ignore + name: mpod.kb.io + rules: + - apiGroups: + - "" + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - pods + sideEffects: None \ No newline at end of file diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/newrelic_license_secret.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/newrelic_license_secret.yaml new file mode 100644 index 0000000000..db2c35f72b --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/newrelic_license_secret.yaml @@ -0,0 +1,14 @@ +{{- $licenseKey := include "k8s-agents-operator.licenseKey" . -}} +{{- if $licenseKey }} +apiVersion: v1 +kind: Secret +metadata: + name: "newrelic-key-secret" + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "k8s-agents-operator.chart" . }} + app.kubernetes.io/instance: {{ .Release.Name }} +type: Opaque +data: + new_relic_license_key: {{ $licenseKey | b64enc }} +{{- end }} \ No newline at end of file diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/proxy-rbac.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/proxy-rbac.yaml new file mode 100644 index 0000000000..af583f5957 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/proxy-rbac.yaml @@ -0,0 +1,34 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "k8s-agents-operator.fullname" . }}-proxy-role + labels: + {{- include "k8s-agents-operator.labels" . | nindent 4 }} +rules: +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "k8s-agents-operator.fullname" . }}-proxy-rolebinding + labels: + {{- include "k8s-agents-operator.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: '{{ template "k8s-agents-operator.fullname" . }}-proxy-role' +subjects: +- kind: ServiceAccount + name: '{{ template "k8s-agents-operator.serviceAccountName" . }}' + namespace: '{{ .Release.Namespace }}' \ No newline at end of file diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/reader-rbac.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/reader-rbac.yaml new file mode 100644 index 0000000000..6482ff0db6 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/reader-rbac.yaml @@ -0,0 +1,11 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "k8s-agents-operator.fullname" . }}-metrics-reader + labels: + {{- include "k8s-agents-operator.labels" . | nindent 4 }} +rules: +- nonResourceURLs: + - /metrics + verbs: + - get \ No newline at end of file diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/selfsigned-issuer.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/selfsigned-issuer.yaml new file mode 100644 index 0000000000..31c0cc79f9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/selfsigned-issuer.yaml @@ -0,0 +1,8 @@ +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: {{ template "k8s-agents-operator.fullname" . }}-selfsigned-issuer + labels: + {{- include "k8s-agents-operator.labels" . | nindent 4 }} +spec: + selfSigned: {} \ No newline at end of file diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/service.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/service.yaml new file mode 100644 index 0000000000..ac39844310 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "k8s-agents-operator.fullname" . }} + labels: + control-plane: controller-manager + {{- include "k8s-agents-operator.labels" . | nindent 4 }} +spec: + type: {{ .Values.metricsService.type }} + selector: + control-plane: controller-manager + {{- include "k8s-agents-operator.labels" . | nindent 4 }} + ports: + {{- .Values.metricsService.ports | toYaml | nindent 2 -}} \ No newline at end of file diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/validating-webhook-configuration.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/validating-webhook-configuration.yaml new file mode 100644 index 0000000000..f98608b7e6 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/validating-webhook-configuration.yaml @@ -0,0 +1,48 @@ +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: {{ template "k8s-agents-operator.fullname" . }}-validating-webhook-configuration + annotations: + cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ template "k8s-agents-operator.fullname" . }}-serving-cert + labels: + {{- include "k8s-agents-operator.labels" . | nindent 4 }} +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: '{{ template "k8s-agents-operator.fullname" . }}-webhook-service' + namespace: '{{ .Release.Namespace }}' + path: /validate-newrelic-com-v1alpha1-instrumentation + failurePolicy: Fail + name: vinstrumentationcreateupdate.kb.io + rules: + - apiGroups: + - newrelic.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - instrumentations + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: '{{ template "k8s-agents-operator.fullname" . }}-webhook-service' + namespace: '{{ .Release.Namespace }}' + path: /validate-newrelic-com-v1alpha1-instrumentation + failurePolicy: Ignore + name: vinstrumentationdelete.kb.io + rules: + - apiGroups: + - newrelic.com + apiVersions: + - v1alpha1 + operations: + - DELETE + resources: + - instrumentations + sideEffects: None \ No newline at end of file diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/webhook-service.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/webhook-service.yaml new file mode 100644 index 0000000000..589cc96a98 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/templates/webhook-service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "k8s-agents-operator.fullname" . }}-webhook-service + labels: + {{- include "k8s-agents-operator.labels" . | nindent 4 }} +spec: + type: {{ .Values.webhookService.type }} + selector: + control-plane: controller-manager + {{- include "k8s-agents-operator.labels" . | nindent 4 }} + ports: + {{- .Values.webhookService.ports | toYaml | nindent 2 -}} \ No newline at end of file diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/values.yaml new file mode 100644 index 0000000000..7cae82fb8a --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/k8s-agents-operator/values.yaml @@ -0,0 +1,62 @@ +# -- Ingest license key to use +# licenseKey: + +controllerManager: + replicas: 1 + + kubeRbacProxy: + image: + repository: gcr.io/kubebuilder/kube-rbac-proxy + tag: v0.14.0 + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 5m + memory: 64Mi + + manager: + image: + repository: newrelic/k8s-agents-operator + tag: + pullPolicy: + resources: + requests: + cpu: 100m + memory: 64Mi + serviceAccount: + create: true + # -- Source: https://docs.openshift.com/container-platform/4.10/operators/operator_sdk/osdk-leader-election.html + # -- Enable leader election mechanism for protecting against split brain if multiple operator pods/replicas are started + leaderElection: + enabled: true + +kubernetesClusterDomain: cluster.local + +metricsService: + ports: + - name: https + port: 8443 + protocol: TCP + targetPort: https + type: ClusterIP + +webhookService: + ports: + - port: 443 + protocol: TCP + targetPort: 9443 + type: ClusterIP + +# -- Source: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ +# -- SecurityContext holds pod-level security attributes and common container settings +securityContext: + runAsGroup: 65532 + runAsNonRoot: true + runAsUser: 65532 + fsGroup: 65532 + +# -- Admission webhooks make sure only requests with correctly formatted rules will get into the Operator +admissionWebhooks: + create: true diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/.helmignore b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/.helmignore new file mode 100644 index 0000000000..f0c1319444 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/.helmignore @@ -0,0 +1,21 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/Chart.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/Chart.yaml new file mode 100644 index 0000000000..a86cd07e97 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/Chart.yaml @@ -0,0 +1,26 @@ +annotations: + artifacthub.io/license: Apache-2.0 + artifacthub.io/links: | + - name: Chart Source + url: https://github.com/prometheus-community/helm-charts +apiVersion: v2 +appVersion: 2.10.0 +description: Install kube-state-metrics to generate and expose cluster-level metrics +home: https://github.com/kubernetes/kube-state-metrics/ +keywords: +- metric +- monitoring +- prometheus +- kubernetes +maintainers: +- email: tariq.ibrahim@mulesoft.com + name: tariq1890 +- email: manuel@rueg.eu + name: mrueg +- email: david@0xdc.me + name: dotdc +name: kube-state-metrics +sources: +- https://github.com/kubernetes/kube-state-metrics/ +type: application +version: 5.12.1 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/README.md b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/README.md new file mode 100644 index 0000000000..843be89e69 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/README.md @@ -0,0 +1,85 @@ +# kube-state-metrics Helm Chart + +Installs the [kube-state-metrics agent](https://github.com/kubernetes/kube-state-metrics). + +## Get Repository Info + +```console +helm repo add prometheus-community https://prometheus-community.github.io/helm-charts +helm repo update +``` + +_See [helm repo](https://helm.sh/docs/helm/helm_repo/) for command documentation._ + + +## Install Chart + +```console +helm install [RELEASE_NAME] prometheus-community/kube-state-metrics [flags] +``` + +_See [configuration](#configuration) below._ + +_See [helm install](https://helm.sh/docs/helm/helm_install/) for command documentation._ + +## Uninstall Chart + +```console +helm uninstall [RELEASE_NAME] +``` + +This removes all the Kubernetes components associated with the chart and deletes the release. + +_See [helm uninstall](https://helm.sh/docs/helm/helm_uninstall/) for command documentation._ + +## Upgrading Chart + +```console +helm upgrade [RELEASE_NAME] prometheus-community/kube-state-metrics [flags] +``` + +_See [helm upgrade](https://helm.sh/docs/helm/helm_upgrade/) for command documentation._ + +### Migrating from stable/kube-state-metrics and kubernetes/kube-state-metrics + +You can upgrade in-place: + +1. [get repository info](#get-repository-info) +1. [upgrade](#upgrading-chart) your existing release name using the new chart repository + +## Upgrading to v3.0.0 + +v3.0.0 includes kube-state-metrics v2.0, see the [changelog](https://github.com/kubernetes/kube-state-metrics/blob/release-2.0/CHANGELOG.md) for major changes on the application-side. + +The upgraded chart now the following changes: + +* Dropped support for helm v2 (helm v3 or later is required) +* collectors key was renamed to resources +* namespace key was renamed to namespaces + +## Configuration + +See [Customizing the Chart Before Installing](https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing). To see all configurable options with detailed comments: + +```console +helm show values prometheus-community/kube-state-metrics +``` + +### kube-rbac-proxy + +You can enable `kube-state-metrics` endpoint protection using `kube-rbac-proxy`. By setting `kubeRBACProxy.enabled: true`, this chart will deploy one RBAC proxy container per endpoint (metrics & telemetry). +To authorize access, authenticate your requests (via a `ServiceAccount` for example) with a `ClusterRole` attached such as: + +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: kube-state-metrics-read +rules: + - apiGroups: [ "" ] + resources: ["services/kube-state-metrics"] + verbs: + - get +``` + +See [kube-rbac-proxy examples](https://github.com/brancz/kube-rbac-proxy/tree/master/examples/resource-attributes) for more details. diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/NOTES.txt b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/NOTES.txt new file mode 100644 index 0000000000..3589c24ec3 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/NOTES.txt @@ -0,0 +1,23 @@ +kube-state-metrics is a simple service that listens to the Kubernetes API server and generates metrics about the state of the objects. +The exposed metrics can be found here: +https://github.com/kubernetes/kube-state-metrics/blob/master/docs/README.md#exposed-metrics + +The metrics are exported on the HTTP endpoint /metrics on the listening port. +In your case, {{ template "kube-state-metrics.fullname" . }}.{{ template "kube-state-metrics.namespace" . }}.svc.cluster.local:{{ .Values.service.port }}/metrics + +They are served either as plaintext or protobuf depending on the Accept header. +They are designed to be consumed either by Prometheus itself or by a scraper that is compatible with scraping a Prometheus client endpoint. + +{{- if .Values.kubeRBACProxy.enabled}} + +kube-rbac-proxy endpoint protections is enabled: +- Metrics endpoints are now HTTPS +- Ensure that the client authenticates the requests (e.g. via service account) with the following role permissions: +``` +rules: + - apiGroups: [ "" ] + resources: ["services/{{ template "kube-state-metrics.fullname" . }}"] + verbs: + - get +``` +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/_helpers.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/_helpers.tpl new file mode 100644 index 0000000000..a4358c87a1 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/_helpers.tpl @@ -0,0 +1,156 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "kube-state-metrics.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "kube-state-metrics.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create the name of the service account to use +*/}} +{{- define "kube-state-metrics.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} + {{ default (include "kube-state-metrics.fullname" .) .Values.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* +Allow the release namespace to be overridden for multi-namespace deployments in combined charts +*/}} +{{- define "kube-state-metrics.namespace" -}} + {{- if .Values.namespaceOverride -}} + {{- .Values.namespaceOverride -}} + {{- else -}} + {{- .Release.Namespace -}} + {{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "kube-state-metrics.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Generate basic labels +*/}} +{{- define "kube-state-metrics.labels" }} +helm.sh/chart: {{ template "kube-state-metrics.chart" . }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/component: metrics +app.kubernetes.io/part-of: {{ template "kube-state-metrics.name" . }} +{{- include "kube-state-metrics.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +{{- if .Values.customLabels }} +{{ toYaml .Values.customLabels }} +{{- end }} +{{- if .Values.releaseLabel }} +release: {{ .Release.Name }} +{{- end }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "kube-state-metrics.selectorLabels" }} +{{- if .Values.selectorOverride }} +{{ toYaml .Values.selectorOverride }} +{{- else }} +app.kubernetes.io/name: {{ include "kube-state-metrics.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} +{{- end }} + +{{/* Sets default scrape limits for servicemonitor */}} +{{- define "servicemonitor.scrapeLimits" -}} +{{- with .sampleLimit }} +sampleLimit: {{ . }} +{{- end }} +{{- with .targetLimit }} +targetLimit: {{ . }} +{{- end }} +{{- with .labelLimit }} +labelLimit: {{ . }} +{{- end }} +{{- with .labelNameLengthLimit }} +labelNameLengthLimit: {{ . }} +{{- end }} +{{- with .labelValueLengthLimit }} +labelValueLengthLimit: {{ . }} +{{- end }} +{{- end -}} + +{{/* +Formats imagePullSecrets. Input is (dict "Values" .Values "imagePullSecrets" .{specific imagePullSecrets}) +*/}} +{{- define "kube-state-metrics.imagePullSecrets" -}} +{{- range (concat .Values.global.imagePullSecrets .imagePullSecrets) }} + {{- if eq (typeOf .) "map[string]interface {}" }} +- {{ toYaml . | trim }} + {{- else }} +- name: {{ . }} + {{- end }} +{{- end }} +{{- end -}} + +{{/* +The image to use for kube-state-metrics +*/}} +{{- define "kube-state-metrics.image" -}} +{{- if .Values.image.sha }} +{{- if .Values.global.imageRegistry }} +{{- printf "%s/%s:%s@%s" .Values.global.imageRegistry .Values.image.repository (default (printf "v%s" .Chart.AppVersion) .Values.image.tag) .Values.image.sha }} +{{- else }} +{{- printf "%s/%s:%s@%s" .Values.image.registry .Values.image.repository (default (printf "v%s" .Chart.AppVersion) .Values.image.tag) .Values.image.sha }} +{{- end }} +{{- else }} +{{- if .Values.global.imageRegistry }} +{{- printf "%s/%s:%s" .Values.global.imageRegistry .Values.image.repository (default (printf "v%s" .Chart.AppVersion) .Values.image.tag) }} +{{- else }} +{{- printf "%s/%s:%s" .Values.image.registry .Values.image.repository (default (printf "v%s" .Chart.AppVersion) .Values.image.tag) }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +The image to use for kubeRBACProxy +*/}} +{{- define "kubeRBACProxy.image" -}} +{{- if .Values.kubeRBACProxy.image.sha }} +{{- if .Values.global.imageRegistry }} +{{- printf "%s/%s:%s@%s" .Values.global.imageRegistry .Values.kubeRBACProxy.image.repository (default (printf "v%s" .Chart.AppVersion) .Values.kubeRBACProxy.image.tag) .Values.kubeRBACProxy.image.sha }} +{{- else }} +{{- printf "%s/%s:%s@%s" .Values.kubeRBACProxy.image.registry .Values.kubeRBACProxy.image.repository (default (printf "v%s" .Chart.AppVersion) .Values.kubeRBACProxy.image.tag) .Values.kubeRBACProxy.image.sha }} +{{- end }} +{{- else }} +{{- if .Values.global.imageRegistry }} +{{- printf "%s/%s:%s" .Values.global.imageRegistry .Values.kubeRBACProxy.image.repository (default (printf "v%s" .Chart.AppVersion) .Values.kubeRBACProxy.image.tag) }} +{{- else }} +{{- printf "%s/%s:%s" .Values.kubeRBACProxy.image.registry .Values.kubeRBACProxy.image.repository (default (printf "v%s" .Chart.AppVersion) .Values.kubeRBACProxy.image.tag) }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/ciliumnetworkpolicy.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/ciliumnetworkpolicy.yaml new file mode 100644 index 0000000000..025cd47a88 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/ciliumnetworkpolicy.yaml @@ -0,0 +1,33 @@ +{{- if and .Values.networkPolicy.enabled (eq .Values.networkPolicy.flavor "cilium") }} +apiVersion: cilium.io/v2 +kind: CiliumNetworkPolicy +metadata: + {{- if .Values.annotations }} + annotations: + {{ toYaml .Values.annotations | nindent 4 }} + {{- end }} + labels: + {{- include "kube-state-metrics.labels" . | indent 4 }} + name: {{ template "kube-state-metrics.fullname" . }} + namespace: {{ template "kube-state-metrics.namespace" . }} +spec: + endpointSelector: + matchLabels: + {{- include "kube-state-metrics.selectorLabels" . | indent 6 }} + egress: + {{- if and .Values.networkPolicy.cilium .Values.networkPolicy.cilium.kubeApiServerSelector }} + {{ toYaml .Values.networkPolicy.cilium.kubeApiServerSelector | nindent 6 }} + {{- else }} + - toEntities: + - kube-apiserver + {{- end }} + ingress: + - toPorts: + - ports: + - port: {{ .Values.service.port | quote }} + protocol: TCP + {{- if .Values.selfMonitor.enabled }} + - port: {{ .Values.selfMonitor.telemetryPort | default 8081 | quote }} + protocol: TCP + {{ end }} +{{ end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/clusterrolebinding.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/clusterrolebinding.yaml new file mode 100644 index 0000000000..cf9f628d04 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/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/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/crs-configmap.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/crs-configmap.yaml new file mode 100644 index 0000000000..d38a75a51d --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/crs-configmap.yaml @@ -0,0 +1,16 @@ +{{- if .Values.customResourceState.enabled}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "kube-state-metrics.fullname" . }}-customresourcestate-config + namespace: {{ template "kube-state-metrics.namespace" . }} + labels: + {{- include "kube-state-metrics.labels" . | indent 4 }} + {{- if .Values.annotations }} + annotations: + {{ toYaml .Values.annotations | nindent 4 }} + {{- end }} +data: + config.yaml: | + {{- toYaml .Values.customResourceState.config | nindent 4 }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/deployment.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/deployment.yaml new file mode 100644 index 0000000000..31aa610181 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/deployment.yaml @@ -0,0 +1,279 @@ +apiVersion: apps/v1 +{{- if .Values.autosharding.enabled }} +kind: StatefulSet +{{- else }} +kind: Deployment +{{- end }} +metadata: + name: {{ template "kube-state-metrics.fullname" . }} + namespace: {{ template "kube-state-metrics.namespace" . }} + labels: + {{- include "kube-state-metrics.labels" . | indent 4 }} + {{- if .Values.annotations }} + annotations: +{{ toYaml .Values.annotations | indent 4 }} + {{- end }} +spec: + selector: + matchLabels: + {{- include "kube-state-metrics.selectorLabels" . | indent 6 }} + replicas: {{ .Values.replicas }} + revisionHistoryLimit: {{ .Values.revisionHistoryLimit }} + {{- if .Values.autosharding.enabled }} + serviceName: {{ template "kube-state-metrics.fullname" . }} + volumeClaimTemplates: [] + {{- end }} + template: + metadata: + labels: + {{- include "kube-state-metrics.labels" . | indent 8 }} + {{- 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: {{- omit .Values.securityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + {{- if .Values.priorityClassName }} + priorityClassName: {{ .Values.priorityClassName }} + {{- end }} + containers: + {{- $httpPort := ternary 9090 (.Values.service.port | default 8080) .Values.kubeRBACProxy.enabled}} + {{- $telemetryPort := ternary 9091 (.Values.selfMonitor.telemetryPort | default 8081) .Values.kubeRBACProxy.enabled}} + - name: {{ template "kube-state-metrics.name" . }} + {{- if .Values.autosharding.enabled }} + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + {{- end }} + args: + {{- if .Values.extraArgs }} + {{- .Values.extraArgs | toYaml | nindent 8 }} + {{- end }} + - --port={{ $httpPort }} + {{- if .Values.collectors }} + - --resources={{ .Values.collectors | join "," }} + {{- end }} + {{- if .Values.metricLabelsAllowlist }} + - --metric-labels-allowlist={{ .Values.metricLabelsAllowlist | join "," }} + {{- end }} + {{- if .Values.metricAnnotationsAllowList }} + - --metric-annotations-allowlist={{ .Values.metricAnnotationsAllowList | join "," }} + {{- end }} + {{- if .Values.metricAllowlist }} + - --metric-allowlist={{ .Values.metricAllowlist | join "," }} + {{- end }} + {{- if .Values.metricDenylist }} + - --metric-denylist={{ .Values.metricDenylist | join "," }} + {{- end }} + {{- $namespaces := list }} + {{- if .Values.namespaces }} + {{- range $ns := join "," .Values.namespaces | split "," }} + {{- $namespaces = append $namespaces (tpl $ns $) }} + {{- end }} + {{- end }} + {{- if .Values.releaseNamespace }} + {{- $namespaces = append $namespaces ( include "kube-state-metrics.namespace" . ) }} + {{- end }} + {{- if $namespaces }} + - --namespaces={{ $namespaces | mustUniq | join "," }} + {{- end }} + {{- if .Values.namespacesDenylist }} + - --namespaces-denylist={{ tpl (.Values.namespacesDenylist | join ",") $ }} + {{- end }} + {{- if .Values.autosharding.enabled }} + - --pod=$(POD_NAME) + - --pod-namespace=$(POD_NAMESPACE) + {{- end }} + {{- if .Values.kubeconfig.enabled }} + - --kubeconfig=/opt/k8s/.kube/config + {{- end }} + {{- if .Values.kubeRBACProxy.enabled }} + - --telemetry-host=127.0.0.1 + - --telemetry-port={{ $telemetryPort }} + {{- else }} + {{- if .Values.selfMonitor.telemetryHost }} + - --telemetry-host={{ .Values.selfMonitor.telemetryHost }} + {{- end }} + {{- if .Values.selfMonitor.telemetryPort }} + - --telemetry-port={{ $telemetryPort }} + {{- end }} + {{- if .Values.customResourceState.enabled }} + - --custom-resource-state-config-file=/etc/customresourcestate/config.yaml + {{- end }} + {{- end }} + {{- if or (.Values.kubeconfig.enabled) (.Values.customResourceState.enabled) (.Values.volumeMounts) }} + volumeMounts: + {{- if .Values.kubeconfig.enabled }} + - name: kubeconfig + mountPath: /opt/k8s/.kube/ + readOnly: true + {{- end }} + {{- if .Values.customResourceState.enabled }} + - name: customresourcestate-config + mountPath: /etc/customresourcestate + readOnly: true + {{- end }} + {{- if .Values.volumeMounts }} +{{ toYaml .Values.volumeMounts | indent 8 }} + {{- end }} + {{- end }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + image: {{ include "kube-state-metrics.image" . }} + {{- if eq .Values.kubeRBACProxy.enabled false }} + ports: + - containerPort: {{ .Values.service.port | default 8080}} + name: "http" + {{- if .Values.selfMonitor.enabled }} + - containerPort: {{ $telemetryPort }} + name: "metrics" + {{- end }} + {{- end }} + livenessProbe: + httpGet: + path: /healthz + port: {{ $httpPort }} + initialDelaySeconds: 5 + timeoutSeconds: 5 + readinessProbe: + httpGet: + path: / + port: {{ $httpPort }} + 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.kubeRBACProxy.enabled }} + - name: kube-rbac-proxy-http + args: + {{- if .Values.kubeRBACProxy.extraArgs }} + {{- .Values.kubeRBACProxy.extraArgs | toYaml | nindent 8 }} + {{- end }} + - --secure-listen-address=:{{ .Values.service.port | default 8080}} + - --upstream=http://127.0.0.1:{{ $httpPort }}/ + - --proxy-endpoints-port=8888 + - --config-file=/etc/kube-rbac-proxy-config/config-file.yaml + volumeMounts: + - name: kube-rbac-proxy-config + mountPath: /etc/kube-rbac-proxy-config + {{- with .Values.kubeRBACProxy.volumeMounts }} + {{- toYaml . | nindent 10 }} + {{- end }} + imagePullPolicy: {{ .Values.kubeRBACProxy.image.pullPolicy }} + image: {{ include "kubeRBACProxy.image" . }} + ports: + - containerPort: {{ .Values.service.port | default 8080}} + name: "http" + - containerPort: 8888 + name: "http-healthz" + readinessProbe: + httpGet: + scheme: HTTPS + port: 8888 + path: healthz + initialDelaySeconds: 5 + timeoutSeconds: 5 + {{- if .Values.kubeRBACProxy.resources }} + resources: +{{ toYaml .Values.kubeRBACProxy.resources | indent 10 }} +{{- end }} +{{- if .Values.kubeRBACProxy.containerSecurityContext }} + securityContext: +{{ toYaml .Values.kubeRBACProxy.containerSecurityContext | indent 10 }} +{{- end }} + {{- if .Values.selfMonitor.enabled }} + - name: kube-rbac-proxy-telemetry + args: + {{- if .Values.kubeRBACProxy.extraArgs }} + {{- .Values.kubeRBACProxy.extraArgs | toYaml | nindent 8 }} + {{- end }} + - --secure-listen-address=:{{ .Values.selfMonitor.telemetryPort | default 8081 }} + - --upstream=http://127.0.0.1:{{ $telemetryPort }}/ + - --proxy-endpoints-port=8889 + - --config-file=/etc/kube-rbac-proxy-config/config-file.yaml + volumeMounts: + - name: kube-rbac-proxy-config + mountPath: /etc/kube-rbac-proxy-config + {{- with .Values.kubeRBACProxy.volumeMounts }} + {{- toYaml . | nindent 10 }} + {{- end }} + imagePullPolicy: {{ .Values.kubeRBACProxy.image.pullPolicy }} + image: {{ include "kubeRBACProxy.image" . }} + ports: + - containerPort: {{ .Values.selfMonitor.telemetryPort | default 8081 }} + name: "metrics" + - containerPort: 8889 + name: "metrics-healthz" + readinessProbe: + httpGet: + scheme: HTTPS + port: 8889 + path: healthz + initialDelaySeconds: 5 + timeoutSeconds: 5 + {{- if .Values.kubeRBACProxy.resources }} + resources: +{{ toYaml .Values.kubeRBACProxy.resources | indent 10 }} +{{- end }} +{{- if .Values.kubeRBACProxy.containerSecurityContext }} + securityContext: +{{ toYaml .Values.kubeRBACProxy.containerSecurityContext | indent 10 }} +{{- end }} + {{- end }} + {{- end }} +{{- if or .Values.imagePullSecrets .Values.global.imagePullSecrets }} + imagePullSecrets: + {{- include "kube-state-metrics.imagePullSecrets" (dict "Values" .Values "imagePullSecrets" .Values.imagePullSecrets) | indent 8 }} + {{- end }} + {{- if .Values.affinity }} + affinity: +{{ toYaml .Values.affinity | indent 8 }} + {{- end }} + {{- if .Values.nodeSelector }} + nodeSelector: +{{ toYaml .Values.nodeSelector | indent 8 }} + {{- end }} + {{- if .Values.tolerations }} + tolerations: +{{ toYaml .Values.tolerations | indent 8 }} + {{- end }} + {{- if .Values.topologySpreadConstraints }} + topologySpreadConstraints: +{{ toYaml .Values.topologySpreadConstraints | indent 8 }} + {{- end }} + {{- if or (.Values.kubeconfig.enabled) (.Values.customResourceState.enabled) (.Values.volumes) (.Values.kubeRBACProxy.enabled) }} + volumes: + {{- if .Values.kubeconfig.enabled}} + - name: kubeconfig + secret: + secretName: {{ template "kube-state-metrics.fullname" . }}-kubeconfig + {{- end }} + {{- if .Values.kubeRBACProxy.enabled}} + - name: kube-rbac-proxy-config + configMap: + name: {{ template "kube-state-metrics.fullname" . }}-rbac-config + {{- end }} + {{- if .Values.customResourceState.enabled}} + - name: customresourcestate-config + configMap: + name: {{ template "kube-state-metrics.fullname" . }}-customresourcestate-config + {{- end }} + {{- if .Values.volumes }} +{{ toYaml .Values.volumes | indent 8 }} + {{- end }} + {{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/extra-manifests.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/extra-manifests.yaml new file mode 100644 index 0000000000..567f7bf329 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/extra-manifests.yaml @@ -0,0 +1,4 @@ +{{ range .Values.extraManifests }} +--- +{{ tpl (toYaml .) $ }} +{{ end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/kubeconfig-secret.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/kubeconfig-secret.yaml new file mode 100644 index 0000000000..6af0084502 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/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/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/networkpolicy.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/networkpolicy.yaml new file mode 100644 index 0000000000..309b38ec54 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/networkpolicy.yaml @@ -0,0 +1,43 @@ +{{- if and .Values.networkPolicy.enabled (eq .Values.networkPolicy.flavor "kubernetes") }} +kind: NetworkPolicy +apiVersion: networking.k8s.io/v1 +metadata: + {{- if .Values.annotations }} + annotations: + {{ toYaml .Values.annotations | nindent 4 }} + {{- end }} + labels: + {{- include "kube-state-metrics.labels" . | indent 4 }} + name: {{ template "kube-state-metrics.fullname" . }} + namespace: {{ template "kube-state-metrics.namespace" . }} +spec: + {{- if .Values.networkPolicy.egress }} + ## Deny all egress by default + egress: + {{- toYaml .Values.networkPolicy.egress | nindent 4 }} + {{- end }} + ingress: + {{- if .Values.networkPolicy.ingress }} + {{- toYaml .Values.networkPolicy.ingress | nindent 4 }} + {{- else }} + ## Allow ingress on default ports by default + - ports: + - port: {{ .Values.service.port | default 8080 }} + protocol: TCP + {{- if .Values.selfMonitor.enabled }} + {{- $telemetryPort := ternary 9091 (.Values.selfMonitor.telemetryPort | default 8081) .Values.kubeRBACProxy.enabled}} + - port: {{ $telemetryPort }} + protocol: TCP + {{- end }} + {{- end }} + podSelector: + {{- if .Values.networkPolicy.podSelector }} + {{- toYaml .Values.networkPolicy.podSelector | nindent 4 }} + {{- else }} + matchLabels: + {{- include "kube-state-metrics.selectorLabels" . | indent 6 }} + {{- end }} + policyTypes: + - Ingress + - Egress +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/pdb.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/pdb.yaml new file mode 100644 index 0000000000..3771b511de --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/pdb.yaml @@ -0,0 +1,18 @@ +{{- if .Values.podDisruptionBudget -}} +{{ if $.Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" -}} +apiVersion: policy/v1 +{{- else -}} +apiVersion: policy/v1beta1 +{{- end }} +kind: PodDisruptionBudget +metadata: + name: {{ template "kube-state-metrics.fullname" . }} + namespace: {{ template "kube-state-metrics.namespace" . }} + labels: + {{- include "kube-state-metrics.labels" . | indent 4 }} +spec: + selector: + matchLabels: + app.kubernetes.io/name: {{ template "kube-state-metrics.name" . }} +{{ toYaml .Values.podDisruptionBudget | indent 2 }} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/podsecuritypolicy.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/podsecuritypolicy.yaml new file mode 100644 index 0000000000..8905e113e8 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/podsecuritypolicy.yaml @@ -0,0 +1,39 @@ +{{- if and .Values.podSecurityPolicy.enabled (.Capabilities.APIVersions.Has "policy/v1beta1/PodSecurityPolicy") }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "kube-state-metrics.fullname" . }} + labels: + {{- include "kube-state-metrics.labels" . | indent 4 }} +{{- if .Values.podSecurityPolicy.annotations }} + annotations: +{{ toYaml .Values.podSecurityPolicy.annotations | indent 4 }} +{{- end }} +spec: + privileged: false + volumes: + - 'secret' +{{- if .Values.podSecurityPolicy.additionalVolumes }} +{{ toYaml .Values.podSecurityPolicy.additionalVolumes | indent 4 }} +{{- end }} + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'MustRunAsNonRoot' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'MustRunAs' + ranges: + # Forbid adding the root group. + - min: 1 + max: 65535 + fsGroup: + rule: 'MustRunAs' + ranges: + # Forbid adding the root group. + - min: 1 + max: 65535 + readOnlyRootFilesystem: false +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/psp-clusterrole.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/psp-clusterrole.yaml new file mode 100644 index 0000000000..654e4a3d57 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/psp-clusterrole.yaml @@ -0,0 +1,19 @@ +{{- if and .Values.podSecurityPolicy.enabled (.Capabilities.APIVersions.Has "policy/v1beta1/PodSecurityPolicy") }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + {{- include "kube-state-metrics.labels" . | indent 4 }} + name: psp-{{ template "kube-state-metrics.fullname" . }} +rules: +{{- $kubeTargetVersion := default .Capabilities.KubeVersion.GitVersion .Values.kubeTargetVersionOverride }} +{{- if semverCompare "> 1.15.0-0" $kubeTargetVersion }} +- apiGroups: ['policy'] +{{- else }} +- apiGroups: ['extensions'] +{{- end }} + resources: ['podsecuritypolicies'] + verbs: ['use'] + resourceNames: + - {{ template "kube-state-metrics.fullname" . }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/psp-clusterrolebinding.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/psp-clusterrolebinding.yaml new file mode 100644 index 0000000000..5b62a18bdf --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/psp-clusterrolebinding.yaml @@ -0,0 +1,16 @@ +{{- if and .Values.podSecurityPolicy.enabled (.Capabilities.APIVersions.Has "policy/v1beta1/PodSecurityPolicy") }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + {{- include "kube-state-metrics.labels" . | indent 4 }} + name: psp-{{ template "kube-state-metrics.fullname" . }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: psp-{{ template "kube-state-metrics.fullname" . }} +subjects: + - kind: ServiceAccount + name: {{ template "kube-state-metrics.serviceAccountName" . }} + namespace: {{ template "kube-state-metrics.namespace" . }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/rbac-configmap.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/rbac-configmap.yaml new file mode 100644 index 0000000000..671dc9d660 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/rbac-configmap.yaml @@ -0,0 +1,22 @@ +{{- if .Values.kubeRBACProxy.enabled}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "kube-state-metrics.fullname" . }}-rbac-config + namespace: {{ template "kube-state-metrics.namespace" . }} + labels: + {{- include "kube-state-metrics.labels" . | indent 4 }} + {{- if .Values.annotations }} + annotations: + {{ toYaml .Values.annotations | nindent 4 }} + {{- end }} +data: + config-file.yaml: |+ + authorization: + resourceAttributes: + namespace: {{ template "kube-state-metrics.namespace" . }} + apiVersion: v1 + resource: services + subresource: {{ template "kube-state-metrics.fullname" . }} + name: {{ template "kube-state-metrics.fullname" . }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/role.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/role.yaml new file mode 100644 index 0000000000..d33687f2d1 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/role.yaml @@ -0,0 +1,212 @@ +{{- if and (eq .Values.rbac.create true) (not .Values.rbac.useExistingRole) -}} +{{- range (ternary (join "," .Values.namespaces | split "," ) (list "") (eq $.Values.rbac.useClusterRole false)) }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +{{- if eq $.Values.rbac.useClusterRole false }} +kind: Role +{{- else }} +kind: ClusterRole +{{- end }} +metadata: + labels: + {{- include "kube-state-metrics.labels" $ | indent 4 }} + name: {{ template "kube-state-metrics.fullname" $ }} +{{- if eq $.Values.rbac.useClusterRole false }} + namespace: {{ . }} +{{- end }} +rules: +{{ if has "certificatesigningrequests" $.Values.collectors }} +- apiGroups: ["certificates.k8s.io"] + resources: + - certificatesigningrequests + verbs: ["list", "watch"] +{{ end -}} +{{ if has "configmaps" $.Values.collectors }} +- apiGroups: [""] + resources: + - configmaps + verbs: ["list", "watch"] +{{ end -}} +{{ if has "cronjobs" $.Values.collectors }} +- apiGroups: ["batch"] + resources: + - cronjobs + verbs: ["list", "watch"] +{{ end -}} +{{ if has "daemonsets" $.Values.collectors }} +- apiGroups: ["extensions", "apps"] + resources: + - daemonsets + verbs: ["list", "watch"] +{{ end -}} +{{ if has "deployments" $.Values.collectors }} +- apiGroups: ["extensions", "apps"] + resources: + - deployments + verbs: ["list", "watch"] +{{ end -}} +{{ if has "endpoints" $.Values.collectors }} +- apiGroups: [""] + resources: + - endpoints + verbs: ["list", "watch"] +{{ end -}} +{{ if has "endpointslices" $.Values.collectors }} +- apiGroups: ["discovery.k8s.io"] + resources: + - endpointslices + verbs: ["list", "watch"] +{{ end -}} +{{ if has "horizontalpodautoscalers" $.Values.collectors }} +- apiGroups: ["autoscaling"] + resources: + - horizontalpodautoscalers + verbs: ["list", "watch"] +{{ end -}} +{{ if has "ingresses" $.Values.collectors }} +- apiGroups: ["extensions", "networking.k8s.io"] + resources: + - ingresses + verbs: ["list", "watch"] +{{ end -}} +{{ if has "jobs" $.Values.collectors }} +- apiGroups: ["batch"] + resources: + - jobs + verbs: ["list", "watch"] +{{ end -}} +{{ if has "leases" $.Values.collectors }} +- apiGroups: ["coordination.k8s.io"] + resources: + - leases + verbs: ["list", "watch"] +{{ end -}} +{{ if has "limitranges" $.Values.collectors }} +- apiGroups: [""] + resources: + - limitranges + verbs: ["list", "watch"] +{{ end -}} +{{ if has "mutatingwebhookconfigurations" $.Values.collectors }} +- apiGroups: ["admissionregistration.k8s.io"] + resources: + - mutatingwebhookconfigurations + verbs: ["list", "watch"] +{{ end -}} +{{ if has "namespaces" $.Values.collectors }} +- apiGroups: [""] + resources: + - namespaces + verbs: ["list", "watch"] +{{ end -}} +{{ if has "networkpolicies" $.Values.collectors }} +- apiGroups: ["networking.k8s.io"] + resources: + - networkpolicies + verbs: ["list", "watch"] +{{ end -}} +{{ if has "nodes" $.Values.collectors }} +- apiGroups: [""] + resources: + - nodes + verbs: ["list", "watch"] +{{ end -}} +{{ if has "persistentvolumeclaims" $.Values.collectors }} +- apiGroups: [""] + resources: + - persistentvolumeclaims + verbs: ["list", "watch"] +{{ end -}} +{{ if has "persistentvolumes" $.Values.collectors }} +- apiGroups: [""] + resources: + - persistentvolumes + verbs: ["list", "watch"] +{{ end -}} +{{ if has "poddisruptionbudgets" $.Values.collectors }} +- apiGroups: ["policy"] + resources: + - poddisruptionbudgets + verbs: ["list", "watch"] +{{ end -}} +{{ if has "pods" $.Values.collectors }} +- apiGroups: [""] + resources: + - pods + verbs: ["list", "watch"] +{{ end -}} +{{ if has "replicasets" $.Values.collectors }} +- apiGroups: ["extensions", "apps"] + resources: + - replicasets + verbs: ["list", "watch"] +{{ end -}} +{{ if has "replicationcontrollers" $.Values.collectors }} +- apiGroups: [""] + resources: + - replicationcontrollers + verbs: ["list", "watch"] +{{ end -}} +{{ if has "resourcequotas" $.Values.collectors }} +- apiGroups: [""] + resources: + - resourcequotas + verbs: ["list", "watch"] +{{ end -}} +{{ if has "secrets" $.Values.collectors }} +- apiGroups: [""] + resources: + - secrets + verbs: ["list", "watch"] +{{ end -}} +{{ if has "services" $.Values.collectors }} +- apiGroups: [""] + resources: + - services + verbs: ["list", "watch"] +{{ end -}} +{{ if has "statefulsets" $.Values.collectors }} +- apiGroups: ["apps"] + resources: + - statefulsets + verbs: ["list", "watch"] +{{ end -}} +{{ if has "storageclasses" $.Values.collectors }} +- apiGroups: ["storage.k8s.io"] + resources: + - storageclasses + verbs: ["list", "watch"] +{{ end -}} +{{ if has "validatingwebhookconfigurations" $.Values.collectors }} +- apiGroups: ["admissionregistration.k8s.io"] + resources: + - validatingwebhookconfigurations + verbs: ["list", "watch"] +{{ end -}} +{{ if has "volumeattachments" $.Values.collectors }} +- apiGroups: ["storage.k8s.io"] + resources: + - volumeattachments + verbs: ["list", "watch"] +{{ end -}} +{{- if $.Values.kubeRBACProxy.enabled }} +- apiGroups: ["authentication.k8s.io"] + resources: + - tokenreviews + verbs: ["create"] +- apiGroups: ["authorization.k8s.io"] + resources: + - subjectaccessreviews + verbs: ["create"] +{{- end }} +{{- if $.Values.customResourceState.enabled }} +- apiGroups: ["apiextensions.k8s.io"] + resources: + - customresourcedefinitions + verbs: ["list", "watch"] +{{- end }} +{{ if $.Values.rbac.extraRules }} +{{ toYaml $.Values.rbac.extraRules }} +{{ end }} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/rolebinding.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/rolebinding.yaml new file mode 100644 index 0000000000..330651b73f --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/rolebinding.yaml @@ -0,0 +1,24 @@ +{{- if and (eq .Values.rbac.create true) (eq .Values.rbac.useClusterRole false) -}} +{{- range (join "," $.Values.namespaces) | split "," }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + {{- include "kube-state-metrics.labels" $ | indent 4 }} + name: {{ template "kube-state-metrics.fullname" $ }} + namespace: {{ . }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role +{{- if (not $.Values.rbac.useExistingRole) }} + name: {{ template "kube-state-metrics.fullname" $ }} +{{- else }} + name: {{ $.Values.rbac.useExistingRole }} +{{- end }} +subjects: +- kind: ServiceAccount + name: {{ template "kube-state-metrics.serviceAccountName" $ }} + namespace: {{ template "kube-state-metrics.namespace" $ }} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/service.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/service.yaml new file mode 100644 index 0000000000..6c486a662a --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/service.yaml @@ -0,0 +1,49 @@ +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 }} + {{- if .Values.selfMonitor.telemetryNodePort }} + nodePort: {{ .Values.selfMonitor.telemetryNodePort }} + {{- end }} + {{ end }} +{{- if .Values.service.loadBalancerIP }} + loadBalancerIP: "{{ .Values.service.loadBalancerIP }}" +{{- end }} +{{- if .Values.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: + {{- range $cidr := .Values.service.loadBalancerSourceRanges }} + - {{ $cidr }} + {{- end }} +{{- end }} +{{- if .Values.autosharding.enabled }} + clusterIP: None +{{- else if .Values.service.clusterIP }} + clusterIP: "{{ .Values.service.clusterIP }}" +{{- end }} + selector: + {{- include "kube-state-metrics.selectorLabels" . | indent 4 }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/serviceaccount.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/serviceaccount.yaml new file mode 100644 index 0000000000..a7ff4dd3d7 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/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: + {{- include "kube-state-metrics.imagePullSecrets" (dict "Values" .Values "imagePullSecrets" .Values.serviceAccount.imagePullSecrets) | indent 2 }} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/servicemonitor.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/servicemonitor.yaml new file mode 100644 index 0000000000..79a07a6555 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/servicemonitor.yaml @@ -0,0 +1,114 @@ +{{- 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 }} + {{- with .Values.prometheus.monitor.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + jobLabel: {{ default "app.kubernetes.io/name" .Values.prometheus.monitor.jobLabel }} + {{- with .Values.prometheus.monitor.targetLabels }} + targetLabels: + {{- toYaml . | trim | nindent 4 }} + {{- end }} + {{- with .Values.prometheus.monitor.podTargetLabels }} + podTargetLabels: + {{- toYaml . | trim | nindent 4 }} + {{- end }} + {{- include "servicemonitor.scrapeLimits" .Values.prometheus.monitor | indent 2 }} + {{- if .Values.prometheus.monitor.namespaceSelector }} + namespaceSelector: + matchNames: + {{- with .Values.prometheus.monitor.namespaceSelector }} + {{- toYaml . | nindent 6 }} + {{- end }} + {{- end }} + selector: + matchLabels: + {{- with .Values.prometheus.monitor.selectorOverride }} + {{- toYaml . | nindent 6 }} + {{- else }} + {{- include "kube-state-metrics.selectorLabels" . | indent 6 }} + {{- end }} + endpoints: + - port: http + {{- if .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.prometheus.monitor.scheme }} + scheme: {{ .Values.prometheus.monitor.scheme }} + {{- end }} + {{- if .Values.prometheus.monitor.tlsConfig }} + tlsConfig: + {{- toYaml .Values.prometheus.monitor.tlsConfig | nindent 8 }} + {{- end }} + {{- if .Values.prometheus.monitor.bearerTokenFile }} + bearerTokenFile: {{ .Values.prometheus.monitor.bearerTokenFile }} + {{- end }} + {{- with .Values.prometheus.monitor.bearerTokenSecret }} + bearerTokenSecret: + {{- toYaml . | 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 }} + {{- if .Values.prometheus.monitor.scheme }} + scheme: {{ .Values.prometheus.monitor.scheme }} + {{- end }} + {{- if .Values.prometheus.monitor.tlsConfig }} + tlsConfig: + {{- toYaml .Values.prometheus.monitor.tlsConfig | nindent 8 }} + {{- end }} + {{- if .Values.prometheus.monitor.bearerTokenFile }} + bearerTokenFile: {{ .Values.prometheus.monitor.bearerTokenFile }} + {{- end }} + {{- with .Values.prometheus.monitor.bearerTokenSecret }} + bearerTokenSecret: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/stsdiscovery-role.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/stsdiscovery-role.yaml new file mode 100644 index 0000000000..489de147c1 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/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/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/stsdiscovery-rolebinding.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/stsdiscovery-rolebinding.yaml new file mode 100644 index 0000000000..73b37a4f64 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/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/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/verticalpodautoscaler.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/verticalpodautoscaler.yaml new file mode 100644 index 0000000000..f46305b517 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/templates/verticalpodautoscaler.yaml @@ -0,0 +1,44 @@ +{{- if and (.Capabilities.APIVersions.Has "autoscaling.k8s.io/v1") (.Values.verticalPodAutoscaler.enabled) }} +apiVersion: autoscaling.k8s.io/v1 +kind: VerticalPodAutoscaler +metadata: + name: {{ template "kube-state-metrics.fullname" . }} + namespace: {{ template "kube-state-metrics.namespace" . }} + labels: + {{- include "kube-state-metrics.labels" . | indent 4 }} +spec: + {{- with .Values.verticalPodAutoscaler.recommenders }} + recommenders: + {{- toYaml . | nindent 4 }} + {{- end }} + resourcePolicy: + containerPolicies: + - containerName: {{ template "kube-state-metrics.name" . }} + {{- with .Values.verticalPodAutoscaler.controlledResources }} + controlledResources: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.verticalPodAutoscaler.controlledValues }} + controlledValues: {{ .Values.verticalPodAutoscaler.controlledValues }} + {{- end }} + {{- if .Values.verticalPodAutoscaler.maxAllowed }} + maxAllowed: + {{ toYaml .Values.verticalPodAutoscaler.maxAllowed | nindent 8 }} + {{- end }} + {{- if .Values.verticalPodAutoscaler.minAllowed }} + minAllowed: + {{ toYaml .Values.verticalPodAutoscaler.minAllowed | nindent 8 }} + {{- end }} + targetRef: + apiVersion: apps/v1 + {{- if .Values.autosharding.enabled }} + kind: StatefulSet + {{- else }} + kind: Deployment + {{- end }} + name: {{ template "kube-state-metrics.fullname" . }} + {{- with .Values.verticalPodAutoscaler.updatePolicy }} + updatePolicy: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/values.yaml new file mode 100644 index 0000000000..00eabab6ab --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/kube-state-metrics/values.yaml @@ -0,0 +1,441 @@ +# Default values for kube-state-metrics. +prometheusScrape: true +image: + registry: registry.k8s.io + repository: kube-state-metrics/kube-state-metrics + # If unset use v + .Charts.appVersion + tag: "" + sha: "" + pullPolicy: IfNotPresent + +imagePullSecrets: [] +# - name: "image-pull-secret" + +global: + # To help compatibility with other charts which use global.imagePullSecrets. + # Allow either an array of {name: pullSecret} maps (k8s-style), or an array of strings (more common helm-style). + # global: + # imagePullSecrets: + # - name: pullSecret1 + # - name: pullSecret2 + # or + # global: + # imagePullSecrets: + # - pullSecret1 + # - pullSecret2 + imagePullSecrets: [] + # + # Allow parent charts to override registry hostname + imageRegistry: "" + +# If set to true, this will deploy kube-state-metrics as a StatefulSet and the data +# will be automatically sharded across <.Values.replicas> pods using the built-in +# autodiscovery feature: https://github.com/kubernetes/kube-state-metrics#automated-sharding +# This is an experimental feature and there are no stability guarantees. +autosharding: + enabled: false + +replicas: 1 + +# Number of old history to retain to allow rollback +# Default Kubernetes value is set to 10 +revisionHistoryLimit: 10 + +# List of additional cli arguments to configure kube-state-metrics +# for example: --enable-gzip-encoding, --log-file, etc. +# all the possible args can be found here: https://github.com/kubernetes/kube-state-metrics/blob/master/docs/cli-arguments.md +extraArgs: [] + +service: + port: 8080 + # Default to clusterIP for backward compatibility + type: ClusterIP + nodePort: 0 + loadBalancerIP: "" + # Only allow access to the loadBalancerIP from these IPs + loadBalancerSourceRanges: [] + clusterIP: "" + annotations: {} + +## Additional labels to add to all resources +customLabels: {} + # app: kube-state-metrics + +## Override selector labels +selectorOverride: {} + +## set to true to add the release label so scraping of the servicemonitor with kube-prometheus-stack works out of the box +releaseLabel: false + +hostNetwork: false + +rbac: + # If true, create & use RBAC resources + create: true + + # Set to a rolename to use existing role - skipping role creating - but still doing serviceaccount and rolebinding to it, rolename set here. + # useExistingRole: your-existing-role + + # If set to false - Run without Cluteradmin privs needed - ONLY works if namespace is also set (if useExistingRole is set this name is used as ClusterRole or Role to bind to) + useClusterRole: true + + # Add permissions for CustomResources' apiGroups in Role/ClusterRole. Should be used in conjunction with Custom Resource State Metrics configuration + # Example: + # - apiGroups: ["monitoring.coreos.com"] + # resources: ["prometheuses"] + # verbs: ["list", "watch"] + extraRules: [] + +# Configure kube-rbac-proxy. When enabled, creates one kube-rbac-proxy container per exposed HTTP endpoint (metrics and telemetry if enabled). +# The requests are served through the same service but requests are then HTTPS. +kubeRBACProxy: + enabled: false + image: + registry: quay.io + repository: brancz/kube-rbac-proxy + tag: v0.14.0 + sha: "" + pullPolicy: IfNotPresent + + # List of additional cli arguments to configure kube-rbac-prxy + # for example: --tls-cipher-suites, --log-file, etc. + # all the possible args can be found here: https://github.com/brancz/kube-rbac-proxy#usage + extraArgs: [] + + ## Specify security settings for a Container + ## Allows overrides and additional options compared to (Pod) securityContext + ## Ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container + containerSecurityContext: {} + + resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 64Mi + # requests: + # cpu: 10m + # memory: 32Mi + + ## volumeMounts enables mounting custom volumes in rbac-proxy containers + ## Useful for TLS certificates and keys + volumeMounts: [] + # - mountPath: /etc/tls + # name: kube-rbac-proxy-tls + # readOnly: true + +serviceAccount: + # Specifies whether a ServiceAccount should be created, require rbac true + create: true + # The name of the ServiceAccount to use. + # If not set and create is true, a name is generated using the fullname template + name: + # Reference to one or more secrets to be used when pulling images + # ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + imagePullSecrets: [] + # ServiceAccount annotations. + # Use case: AWS EKS IAM roles for service accounts + # ref: https://docs.aws.amazon.com/eks/latest/userguide/specify-service-account-role.html + annotations: {} + +prometheus: + monitor: + enabled: false + annotations: {} + additionalLabels: {} + namespace: "" + namespaceSelector: [] + jobLabel: "" + targetLabels: [] + podTargetLabels: [] + interval: "" + ## SampleLimit defines per-scrape limit on number of scraped samples that will be accepted. + ## + sampleLimit: 0 + + ## TargetLimit defines a limit on the number of scraped targets that will be accepted. + ## + targetLimit: 0 + + ## Per-scrape limit on number of labels that will be accepted for a sample. Only valid in Prometheus versions 2.27.0 and newer. + ## + labelLimit: 0 + + ## Per-scrape limit on length of labels name that will be accepted for a sample. Only valid in Prometheus versions 2.27.0 and newer. + ## + labelNameLengthLimit: 0 + + ## Per-scrape limit on length of labels value that will be accepted for a sample. Only valid in Prometheus versions 2.27.0 and newer. + ## + labelValueLengthLimit: 0 + scrapeTimeout: "" + proxyUrl: "" + selectorOverride: {} + honorLabels: false + metricRelabelings: [] + relabelings: [] + scheme: "" + ## File to read bearer token for scraping targets + bearerTokenFile: "" + ## Secret to mount to read bearer token for scraping targets. The secret needs + ## to be in the same namespace as the service monitor and accessible by the + ## Prometheus Operator + bearerTokenSecret: {} + # name: secret-name + # key: key-name + tlsConfig: {} + +## Specify if a Pod Security Policy for kube-state-metrics must be created +## Ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/ +## +podSecurityPolicy: + enabled: false + annotations: {} + ## Specify pod annotations + ## Ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/#apparmor + ## Ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/#seccomp + ## Ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/#sysctl + ## + # seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*' + # seccomp.security.alpha.kubernetes.io/defaultProfileName: 'docker/default' + # apparmor.security.beta.kubernetes.io/defaultProfileName: 'runtime/default' + + additionalVolumes: [] + +## Configure network policy for kube-state-metrics +networkPolicy: + enabled: false + # networkPolicy.flavor -- Flavor of the network policy to use. + # Can be: + # * kubernetes for networking.k8s.io/v1/NetworkPolicy + # * cilium for cilium.io/v2/CiliumNetworkPolicy + flavor: kubernetes + + ## Configure the cilium network policy kube-apiserver selector + # cilium: + # kubeApiServerSelector: + # - toEntities: + # - kube-apiserver + + # egress: + # - {} + # ingress: + # - {} + # podSelector: + # matchLabels: + # app.kubernetes.io/name: kube-state-metrics + +securityContext: + enabled: true + runAsGroup: 65534 + runAsUser: 65534 + fsGroup: 65534 + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + +## Specify security settings for a Container +## Allows overrides and additional options compared to (Pod) securityContext +## Ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container +containerSecurityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + +## Node labels for pod assignment +## Ref: https://kubernetes.io/docs/user-guide/node-selection/ +nodeSelector: {} + +## Affinity settings for pod assignment +## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ +affinity: {} + +## Tolerations for pod assignment +## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ +tolerations: [] + +## Topology spread constraints for pod assignment +## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ +topologySpreadConstraints: [] + +# Annotations to be added to the deployment/statefulset +annotations: {} + +# Annotations to be added to the pod +podAnnotations: {} + +## Assign a PriorityClassName to pods if set +# priorityClassName: "" + +# Ref: https://kubernetes.io/docs/tasks/run-application/configure-pdb/ +podDisruptionBudget: {} + +# Comma-separated list of metrics to be exposed. +# This list comprises of exact metric names and/or regex patterns. +# The allowlist and denylist are mutually exclusive. +metricAllowlist: [] + +# Comma-separated list of metrics not to be enabled. +# This list comprises of exact metric names and/or regex patterns. +# The allowlist and denylist are mutually exclusive. +metricDenylist: [] + +# Comma-separated list of additional Kubernetes label keys that will be used in the resource's +# labels metric. By default the metric contains only name and namespace labels. +# To include additional labels, provide a list of resource names in their plural form and Kubernetes +# label keys you would like to allow for them (Example: '=namespaces=[k8s-label-1,k8s-label-n,...],pods=[app],...)'. +# A single '*' can be provided per resource instead to allow any labels, but that has +# severe performance implications (Example: '=pods=[*]'). +metricLabelsAllowlist: [] + # - namespaces=[k8s-label-1,k8s-label-n] + +# Comma-separated list of Kubernetes annotations keys that will be used in the resource' +# labels metric. By default the metric contains only name and namespace labels. +# To include additional annotations provide a list of resource names in their plural form and Kubernetes +# annotation keys you would like to allow for them (Example: '=namespaces=[kubernetes.io/team,...],pods=[kubernetes.io/team],...)'. +# A single '*' can be provided per resource instead to allow any annotations, but that has +# severe performance implications (Example: '=pods=[*]'). +metricAnnotationsAllowList: [] + # - pods=[k8s-annotation-1,k8s-annotation-n] + +# Available collectors for kube-state-metrics. +# By default, all available resources are enabled, comment out to disable. +collectors: + - certificatesigningrequests + - configmaps + - cronjobs + - daemonsets + - deployments + - endpoints + - horizontalpodautoscalers + - ingresses + - jobs + - leases + - limitranges + - mutatingwebhookconfigurations + - namespaces + - networkpolicies + - nodes + - persistentvolumeclaims + - persistentvolumes + - poddisruptionbudgets + - pods + - replicasets + - replicationcontrollers + - resourcequotas + - secrets + - services + - statefulsets + - storageclasses + - validatingwebhookconfigurations + - volumeattachments + +# Enabling kubeconfig will pass the --kubeconfig argument to the container +kubeconfig: + enabled: false + # base64 encoded kube-config file + secret: + +# Enabling support for customResourceState, will create a configMap including your config that will be read from kube-state-metrics +customResourceState: + enabled: false + # Add (Cluster)Role permissions to list/watch the customResources defined in the config to rbac.extraRules + config: {} + +# Enable only the release namespace for collecting resources. By default all namespaces are collected. +# If releaseNamespace and namespaces are both set a merged list will be collected. +releaseNamespace: false + +# Comma-separated list(string) or yaml list of namespaces to be enabled for collecting resources. By default all namespaces are collected. +namespaces: "" + +# Comma-separated list of namespaces not to be enabled. If namespaces and namespaces-denylist are both set, +# only namespaces that are excluded in namespaces-denylist will be used. +namespacesDenylist: "" + +## Override the deployment namespace +## +namespaceOverride: "" + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 64Mi + # requests: + # cpu: 10m + # memory: 32Mi + +## Provide a k8s version to define apiGroups for podSecurityPolicy Cluster Role. +## For example: kubeTargetVersionOverride: 1.14.9 +## +kubeTargetVersionOverride: "" + +# Enable self metrics configuration for service and Service Monitor +# Default values for telemetry configuration can be overridden +# If you set telemetryNodePort, you must also set service.type to NodePort +selfMonitor: + enabled: false + # telemetryHost: 0.0.0.0 + # telemetryPort: 8081 + # telemetryNodePort: 0 + +# Enable vertical pod autoscaler support for kube-state-metrics +verticalPodAutoscaler: + enabled: false + + # Recommender responsible for generating recommendation for the object. + # List should be empty (then the default recommender will generate the recommendation) + # or contain exactly one recommender. + # recommenders: [] + # - name: custom-recommender-performance + + # List of resources that the vertical pod autoscaler can control. Defaults to cpu and memory + controlledResources: [] + # Specifies which resource values should be controlled: RequestsOnly or RequestsAndLimits. + # controlledValues: RequestsAndLimits + + # Define the max allowed resources for the pod + maxAllowed: {} + # cpu: 200m + # memory: 100Mi + # Define the min allowed resources for the pod + minAllowed: {} + # cpu: 200m + # memory: 100Mi + + # updatePolicy: + # Specifies minimal number of replicas which need to be alive for VPA Updater to attempt pod eviction + # minReplicas: 1 + # Specifies whether recommended updates are applied when a Pod is started and whether recommended updates + # are applied during the life of a Pod. Possible values are "Off", "Initial", "Recreate", and "Auto". + # updateMode: Auto + +# volumeMounts are used to add custom volume mounts to deployment. +# See example below +volumeMounts: [] +# - mountPath: /etc/config +# name: config-volume + +# volumes are used to add custom volumes to deployment +# See example below +volumes: [] +# - configMap: +# name: cm-for-volume +# name: config-volume + +# Extra manifests to deploy as an array +extraManifests: [] + # - apiVersion: v1 + # kind: ConfigMap + # metadata: + # labels: + # name: prometheus-extra + # data: + # extra-data: "value" diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/.helmignore b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/.helmignore new file mode 100644 index 0000000000..f62b5519e5 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/.helmignore @@ -0,0 +1 @@ +templates/admission-webhooks/job-patch/README.md diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/Chart.lock b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/Chart.lock new file mode 100644 index 0000000000..9efaa36a61 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: common-library + repository: https://helm-charts.newrelic.com + version: 1.3.0 +digest: sha256:2e1da613fd8a52706bde45af077779c5d69e9e1641bdf5c982eaf6d1ac67a443 +generated: "2024-08-30T22:48:07.029709954Z" diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/Chart.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/Chart.yaml new file mode 100644 index 0000000000..ded1254499 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/Chart.yaml @@ -0,0 +1,35 @@ +apiVersion: v2 +appVersion: 0.19.2 +dependencies: +- name: common-library + repository: https://helm-charts.newrelic.com + version: 1.3.0 +description: A Helm chart to deploy the New Relic Infrastructure Kubernetes Operator. +home: https://hub.docker.com/r/newrelic/newrelic-infra-operator +icon: https://newrelic.com/themes/custom/curio/assets/mediakit/new_relic_logo_vertical.svg +keywords: +- infrastructure +- newrelic +- monitoring +maintainers: +- name: alvarocabanas + url: https://github.com/alvarocabanas +- name: carlossscastro + url: https://github.com/carlossscastro +- name: sigilioso + url: https://github.com/sigilioso +- name: gsanchezgavier + url: https://github.com/gsanchezgavier +- name: kang-makes + url: https://github.com/kang-makes +- name: marcsanmi + url: https://github.com/marcsanmi +- name: paologallinaharbur + url: https://github.com/paologallinaharbur +- name: roobre + url: https://github.com/roobre +name: newrelic-infra-operator +sources: +- https://github.com/newrelic/newrelic-infra-operator +- https://github.com/newrelic/newrelic-infra-operator/tree/main/charts/newrelic-infra-operator +version: 2.11.2 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/README.md b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/README.md new file mode 100644 index 0000000000..05e8a8d48b --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/README.md @@ -0,0 +1,114 @@ +# newrelic-infra-operator + +A Helm chart to deploy the New Relic Infrastructure Kubernetes Operator. + +**Homepage:** + +## Helm installation + +You can install this chart using [`nri-bundle`](https://github.com/newrelic/helm-charts/tree/master/charts/nri-bundle) located in the +[helm-charts repository](https://github.com/newrelic/helm-charts) or directly from this repository by adding this Helm repository: + +```shell +helm repo add newrelic-infra-operator https://newrelic.github.io/newrelic-infra-operator +helm upgrade --install newrelic-infra-operator/newrelic-infra-operator -f your-custom-values.yaml +``` + +## Source Code + +* +* + +## Usage example + +Make sure you have [added the New Relic chart repository.](../../README.md#install) + +Then, to install this chart, run the following command: + +```sh +helm upgrade --install [release-name] newrelic-infra-operator/newrelic-infra-operator --set cluster=my_cluster_name --set licenseKey [your-license-key] +``` + +When installing on Fargate add as well `--set fargate=true` + +### Configure in which pods the sidecar should be injected + +Policies are available in order to configure in which pods the sidecar should be injected. +Each policy is evaluated independently and if at least one policy matches the operator will inject the sidecar. + +Policies are composed by `namespaceSelector` checking the labels of the Pod namespace, `podSelector` checking +the labels of the Pod and `namespace` checking the namespace name. Each of those, if specified, are ANDed. + +By default, the policies are configured in order to inject the sidecar in each pod belonging to a Fargate profile. + +> Moreover, it is possible to add the label `infra-operator.newrelic.com/disable-injection` to Pods to exclude injection +for a single Pod that otherwise would be selected by the policies. + +Please make sure to configure policies correctly to avoid injecting sidecar for pods running on EC2 nodes +already monitored by the infrastructure DaemonSet. + +### Configure the sidecar with labelsSelectors + +It is also possible to configure `resourceRequirements` and `extraEnvVars` based on the labels of the mutating Pod. + +The current configuration increases the resource requirements for sidecar injected on `KSM` instances. Moreover, +injectes disable the `DISABLE_KUBE_STATE_METRICS` environment variable for Pods not running on `KSM` instances +to decrease the load on the API server. + +## Values managed globally + +This chart implements the [New Relic's common Helm library](https://github.com/newrelic/helm-charts/tree/master/library/common-library) which +means that it honors a wide range of defaults and globals common to most New Relic Helm charts. + +Options that can be defined globally include `affinity`, `nodeSelector`, `tolerations`, `proxy` and others. The full list can be found at +[user's guide of the common library](https://github.com/newrelic/helm-charts/blob/master/library/common-library/README.md). + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| admissionWebhooksPatchJob | object | See `values.yaml` | Image used to create certificates and inject them to the admission webhook | +| admissionWebhooksPatchJob.image.pullSecrets | list | `[]` | The secrets that are needed to pull images from a custom registry. | +| admissionWebhooksPatchJob.volumeMounts | list | `[]` | Volume mounts to add to the job, you might want to mount tmp if Pod Security Policies. Enforce a read-only root. | +| admissionWebhooksPatchJob.volumes | list | `[]` | Volumes to add to the job container. | +| affinity | object | `{}` | Sets pod/node affinities. Can be configured also with `global.affinity` | +| certManager.enabled | bool | `false` | Use cert manager for webhook certs | +| cluster | string | `""` | Name of the Kubernetes cluster monitored. Mandatory. Can be configured also with `global.cluster` | +| config | object | See `values.yaml` | Operator configuration | +| config.ignoreMutationErrors | bool | `true` | IgnoreMutationErrors instruments the operator to ignore injection error instead of failing. If set to false errors of the injection could block the creation of pods. | +| config.infraAgentInjection | object | See `values.yaml` | configuration of the sidecar injection webhook | +| config.infraAgentInjection.agentConfig | object | See `values.yaml` | agentConfig contains the configuration for the container agent injected | +| config.infraAgentInjection.agentConfig.configSelectors | list | See `values.yaml` | configSelectors is the way to configure resource requirements and extra envVars of the injected sidecar container. When mutating it will be applied the first configuration having the labelSelector matching with the mutating pod. | +| config.infraAgentInjection.agentConfig.image | object | See `values.yaml` | Image of the infrastructure agent to be injected. | +| containerSecurityContext | object | `{}` | Sets security context (at container level). Can be configured also with `global.containerSecurityContext` | +| customSecretLicenseKey | string | `""` | In case you don't want to have the license key in you values, this allows you to point to which secret key is the license key located. Can be configured also with `global.customSecretLicenseKey` | +| customSecretName | string | `""` | In case you don't want to have the license key in you values, this allows you to point to a user created secret to get the key from there. Can be configured also with `global.customSecretName` | +| dnsConfig | object | `{}` | Sets pod's dnsConfig. Can be configured also with `global.dnsConfig` | +| fullnameOverride | string | `""` | Override the full name of the release | +| hostNetwork | bool | `false` | Sets pod's hostNetwork. Can be configured also with `global.hostNetwork` | +| image | object | See `values.yaml` | Image for the New Relic Infrastructure Operator | +| image.pullSecrets | list | `[]` | The secrets that are needed to pull images from a custom registry. | +| licenseKey | string | `""` | This set this license key to use. Can be configured also with `global.licenseKey` | +| nameOverride | string | `""` | Override the name of the chart | +| nodeSelector | object | `{}` | Sets pod's node selector. Can be configured also with `global.nodeSelector` | +| podAnnotations | object | `{}` | Annotations to add to the pod. | +| podSecurityContext | object | `{"fsGroup":1001,"runAsGroup":1001,"runAsUser":1001}` | Sets security context (at pod level). Can be configured also with `global.podSecurityContext` | +| priorityClassName | string | `""` | Sets pod's priorityClassName. Can be configured also with `global.priorityClassName` | +| rbac.pspEnabled | bool | `false` | Whether the chart should create Pod Security Policy objects. | +| replicas | int | `1` | | +| resources | object | `{"limits":{"memory":"80M"},"requests":{"cpu":"100m","memory":"30M"}}` | Resources available for this pod | +| serviceAccount | object | See `values.yaml` | Settings controlling ServiceAccount creation | +| serviceAccount.create | bool | `true` | Specifies whether a ServiceAccount should be created | +| timeoutSeconds | int | `10` | Webhook timeout Ref: https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#timeouts | +| tolerations | list | `[]` | Sets pod's tolerations to node taints. Can be configured also with `global.tolerations` | + +## Maintainers + +* [alvarocabanas](https://github.com/alvarocabanas) +* [carlossscastro](https://github.com/carlossscastro) +* [sigilioso](https://github.com/sigilioso) +* [gsanchezgavier](https://github.com/gsanchezgavier) +* [kang-makes](https://github.com/kang-makes) +* [marcsanmi](https://github.com/marcsanmi) +* [paologallinaharbur](https://github.com/paologallinaharbur) +* [roobre](https://github.com/roobre) diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/README.md.gotmpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/README.md.gotmpl new file mode 100644 index 0000000000..1ef6033559 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/README.md.gotmpl @@ -0,0 +1,77 @@ +{{ template "chart.header" . }} +{{ template "chart.deprecationWarning" . }} + +{{ template "chart.description" . }} + +{{ template "chart.homepageLine" . }} + +## Helm installation + +You can install this chart using [`nri-bundle`](https://github.com/newrelic/helm-charts/tree/master/charts/nri-bundle) located in the +[helm-charts repository](https://github.com/newrelic/helm-charts) or directly from this repository by adding this Helm repository: + +```shell +helm repo add newrelic-infra-operator https://newrelic.github.io/newrelic-infra-operator +helm upgrade --install newrelic-infra-operator/newrelic-infra-operator -f your-custom-values.yaml +``` + +{{ template "chart.sourcesSection" . }} + +## Usage example + +Make sure you have [added the New Relic chart repository.](../../README.md#install) + +Then, to install this chart, run the following command: + +```sh +helm upgrade --install [release-name] newrelic-infra-operator/newrelic-infra-operator --set cluster=my_cluster_name --set licenseKey [your-license-key] +``` + +When installing on Fargate add as well `--set fargate=true` + +### Configure in which pods the sidecar should be injected + +Policies are available in order to configure in which pods the sidecar should be injected. +Each policy is evaluated independently and if at least one policy matches the operator will inject the sidecar. + +Policies are composed by `namespaceSelector` checking the labels of the Pod namespace, `podSelector` checking +the labels of the Pod and `namespace` checking the namespace name. Each of those, if specified, are ANDed. + +By default, the policies are configured in order to inject the sidecar in each pod belonging to a Fargate profile. + +> Moreover, it is possible to add the label `infra-operator.newrelic.com/disable-injection` to Pods to exclude injection +for a single Pod that otherwise would be selected by the policies. + +Please make sure to configure policies correctly to avoid injecting sidecar for pods running on EC2 nodes +already monitored by the infrastructure DaemonSet. + +### Configure the sidecar with labelsSelectors + +It is also possible to configure `resourceRequirements` and `extraEnvVars` based on the labels of the mutating Pod. + +The current configuration increases the resource requirements for sidecar injected on `KSM` instances. Moreover, +injectes disable the `DISABLE_KUBE_STATE_METRICS` environment variable for Pods not running on `KSM` instances +to decrease the load on the API server. + +## Values managed globally + +This chart implements the [New Relic's common Helm library](https://github.com/newrelic/helm-charts/tree/master/library/common-library) which +means that it honors a wide range of defaults and globals common to most New Relic Helm charts. + +Options that can be defined globally include `affinity`, `nodeSelector`, `tolerations`, `proxy` and others. The full list can be found at +[user's guide of the common library](https://github.com/newrelic/helm-charts/blob/master/library/common-library/README.md). + +{{ template "chart.valuesSection" . }} + +{{ if .Maintainers }} +## Maintainers +{{ range .Maintainers }} +{{- if .Name }} +{{- if .Url }} +* [{{ .Name }}]({{ .Url }}) +{{- else }} +* {{ .Name }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/.helmignore b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/.helmignore new file mode 100644 index 0000000000..0e8a0eb36f --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/.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 +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/Chart.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/Chart.yaml new file mode 100644 index 0000000000..f2ee5497ee --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/Chart.yaml @@ -0,0 +1,17 @@ +apiVersion: v2 +description: Provides helpers to provide consistency on all the charts +keywords: +- newrelic +- chart-library +maintainers: +- name: juanjjaramillo + url: https://github.com/juanjjaramillo +- name: csongnr + url: https://github.com/csongnr +- name: dbudziwojskiNR + url: https://github.com/dbudziwojskiNR +- name: kang-makes + url: https://github.com/kang-makes +name: common-library +type: library +version: 1.3.0 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/DEVELOPERS.md b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/DEVELOPERS.md new file mode 100644 index 0000000000..7208c673ec --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/DEVELOPERS.md @@ -0,0 +1,747 @@ +# Functions/templates documented for chart writers +Here is some rough documentation separated by the file that contains the function, the function +name and how to use it. We are not covering functions that start with `_` (e.g. +`newrelic.common.license._licenseKey`) because they are used internally by this library for +other helpers. Helm does not have the concept of "public" or "private" functions/templates so +this is a convention of ours. + +## _naming.tpl +These functions are used to name objects. + +### `newrelic.common.naming.name` +This is the same as the idiomatic `CHART-NAME.name` that is created when you use `helm create`. + +It honors `.Values.nameOverride`. + +Usage: +```mustache +{{ include "newrelic.common.naming.name" . }} +``` + +### `newrelic.common.naming.fullname` +This is the same as the idiomatic `CHART-NAME.fullname` that is created when you use `helm create` + +It honors `.Values.fullnameOverride`. + +Usage: +```mustache +{{ include "newrelic.common.naming.fullname" . }} +``` + +### `newrelic.common.naming.chart` +This is the same as the idiomatic `CHART-NAME.chart` that is created when you use `helm create`. + +It is mostly useless for chart writers. It is used internally for templating the labels but there +is no reason to keep it "private". + +Usage: +```mustache +{{ include "newrelic.common.naming.chart" . }} +``` + +### `newrelic.common.naming.truncateToDNS` +This is a useful template that could be used to trim a string to 63 chars and does not end with a dash (`-`). +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). + +Usage: +```mustache +{{ $nameToTruncate := "a-really-really-really-really-REALLY-long-string-that-should-be-truncated-because-it-is-enought-long-to-brak-something" +{{- $truncatedName := include "newrelic.common.naming.truncateToDNS" $nameToTruncate }} +{{- $truncatedName }} +{{- /* This should print: a-really-really-really-really-REALLY-long-string-that-should-be */ -}} +``` + +### `newrelic.common.naming.truncateToDNSWithSuffix` +This template function is the same as the above but instead of receiving a string you should give a `dict` +with a `name` and a `suffix`. This function will join them with a dash (`-`) and trim the `name` so the +result of `name-suffix` is no more than 63 chars + +Usage: +```mustache +{{ $nameToTruncate := "a-really-really-really-really-REALLY-long-string-that-should-be-truncated-because-it-is-enought-long-to-brak-something" +{{- $suffix := "A-NOT-SO-LONG-SUFFIX" }} +{{- $truncatedName := include "truncateToDNSWithSuffix" (dict "name" $nameToTruncate "suffix" $suffix) }} +{{- $truncatedName }} +{{- /* This should print: a-really-really-really-really-REALLY-long-A-NOT-SO-LONG-SUFFIX */ -}} +``` + + + +## _labels.tpl +### `newrelic.common.labels`, `newrelic.common.labels.selectorLabels` and `newrelic.common.labels.podLabels` +These are functions that are used to label objects. They are configured by this `values.yaml` +```yaml +global: + podLabels: {} # included in all the pods of all the charts that implement this library + labels: {} # included in all the objects of all the charts that implement this library +podLabels: {} # included in all the pods of this chart +labels: {} # included in all the objects of this chart +``` + +label maps are merged from global to local values. + +And chart writer should use them like this: +```mustache +metadata: + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +spec: + selector: + matchLabels: + {{- include "newrelic.common.labels.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "newrelic.common.labels.podLabels" . | nindent 8 }} +``` + +`newrelic.common.labels.podLabels` includes `newrelic.common.labels.selectorLabels` automatically. + + + +## _priority-class-name.tpl +### `newrelic.common.priorityClassName` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + priorityClassName: "" +priorityClassName: "" +``` + +Be careful: chart writers should put an empty string (or any kind of Helm falsiness) for this +library to work properly. If in your values a non-falsy `priorityClassName` is found, the global +one is going to be always ignored. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.priorityClassName" . }} + priorityClassName: {{ . }} + {{- end }} +``` + + + +## _hostnetwork.tpl +### `newrelic.common.hostNetwork` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + hostNetwork: # Note that this is empty (nil) +hostNetwork: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `hostNetwork` is defined, the global one is going to be always ignored. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.hostNetwork" . }} + hostNetwork: {{ . }} + {{- end }} +``` + +### `newrelic.common.hostNetwork.value` +This function is an abstraction of the function above but this returns directly "true" or "false". + +Be careful with using this with an `if` as Helm does evaluate "false" (string) as `true`. + +Usage (example in a pod spec): +```mustache +spec: + hostNetwork: {{ include "newrelic.common.hostNetwork.value" . }} +``` + + + +## _dnsconfig.tpl +### `newrelic.common.dnsConfig` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + dnsConfig: {} +dnsConfig: {} +``` + +Be careful: chart writers should put an empty string (or any kind of Helm falsiness) for this +library to work properly. If in your values a non-falsy `dnsConfig` is found, the global +one is going to be always ignored. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.dnsConfig" . }} + dnsConfig: + {{- . | nindent 4 }} + {{- end }} +``` + + + +## _images.tpl +These functions help us to deal with how images are templated. This allows setting `registries` +where to fetch images globally while being flexible enough to fit in different maps of images +and deployments with one or more images. This is the example of a complex `values.yaml` that +we are going to use during the documentation of these functions: + +```yaml +global: + images: + registry: nexus-3-instance.internal.clients-domain.tld +jobImage: + registry: # defaults to "example.tld" when empty in these examples + repository: ingress-nginx/kube-webhook-certgen + tag: v1.1.1 + pullPolicy: IfNotPresent + pullSecrets: [] +images: + integration: + registry: + repository: newrelic/nri-kube-events + tag: 1.8.0 + pullPolicy: IfNotPresent + agent: + registry: + repository: newrelic/k8s-events-forwarder + tag: 1.22.0 + pullPolicy: IfNotPresent + pullSecrets: [] +``` + +### `newrelic.common.images.image` +This will return a string with the image ready to be downloaded that includes the registry, the image and the tag. +`defaultRegistry` is used to keep `registry` field empty in `values.yaml` so you can override the image using +`global.images.registry`, your local `jobImage.registry` and be able to fallback to a registry that is not `docker.io` +(Or the default repository that the client could have set in the CRI). + +Usage: +```mustache +{{- /* For the integration */}} +{{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.images.agent "context" .) }} +{{- /* For jobImage */}} +{{ include "newrelic.common.images.image" ( dict "defaultRegistry" "example.tld" "imageRoot" .Values.jobImage "context" .) }} +``` + +### `newrelic.common.images.registry` +It returns the registry from the global or local values. You should avoid using this helper to create your image +URL and use `newrelic.common.images.image` instead, but it is there to be used in case it is needed. + +Usage: +```mustache +{{- /* For the integration */}} +{{ include "newrelic.common.images.registry" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.registry" ( dict "imageRoot" .Values.images.agent "context" .) }} +{{- /* For jobImage */}} +{{ include "newrelic.common.images.registry" ( dict "defaultRegistry" "example.tld" "imageRoot" .Values.jobImage "context" .) }} +``` + +### `newrelic.common.images.repository` +It returns the image from the values. You should avoid using this helper to create your image +URL and use `newrelic.common.images.image` instead, but it is there to be used in case it is needed. + +Usage: +```mustache +{{- /* For jobImage */}} +{{ include "newrelic.common.images.repository" ( dict "imageRoot" .Values.jobImage "context" .) }} +{{- /* For the integration */}} +{{ include "newrelic.common.images.repository" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.repository" ( dict "imageRoot" .Values.images.agent "context" .) }} +``` + +### `newrelic.common.images.tag` +It returns the image's tag from the values. You should avoid using this helper to create your image +URL and use `newrelic.common.images.image` instead, but it is there to be used in case it is needed. + +Usage: +```mustache +{{- /* For jobImage */}} +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.jobImage "context" .) }} +{{- /* For the integration */}} +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.images.agent "context" .) }} +``` + +### `newrelic.common.images.renderPullSecrets` +If returns a merged map that contains the pull secrets from the global configuration and the local one. + +Usage: +```mustache +{{- /* For jobImage */}} +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" .Values.jobImage.pullSecrets "context" .) }} +{{- /* For the integration */}} +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" .Values.images.pullSecrets "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" .Values.images.pullSecrets "context" .) }} +``` + + + +## _serviceaccount.tpl +These functions are used to evaluate if the service account should be created, with which name and add annotations to it. + +The functions that the common library has implemented for service accounts are: +* `newrelic.common.serviceAccount.create` +* `newrelic.common.serviceAccount.name` +* `newrelic.common.serviceAccount.annotations` + +Usage: +```mustache +{{- if include "newrelic.common.serviceAccount.create" . -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + {{- with (include "newrelic.common.serviceAccount.annotations" .) }} + annotations: + {{- . | nindent 4 }} + {{- end }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "newrelic.common.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} +{{- end }} +``` + + + +## _affinity.tpl, _nodeselector.tpl and _tolerations.tpl +These three files are almost the same and they follow the idiomatic way of `helm create`. + +Each function also looks if there is a global value like the other helpers. +```yaml +global: + affinity: {} + nodeSelector: {} + tolerations: [] +affinity: {} +nodeSelector: {} +tolerations: [] +``` + +The values here are replaced instead of be merged. If a value at root level is found, the global one is ignored. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.nodeSelector" . }} + nodeSelector: + {{- . | nindent 4 }} + {{- end }} + {{- with include "newrelic.common.affinity" . }} + affinity: + {{- . | nindent 4 }} + {{- end }} + {{- with include "newrelic.common.tolerations" . }} + tolerations: + {{- . | nindent 4 }} + {{- end }} +``` + + + +## _agent-config.tpl +### `newrelic.common.agentConfig.defaults` +This returns a YAML that the agent can use directly as a config that includes other options from the values file like verbose mode, +custom attributes, FedRAMP and such. + +Usage: +```mustache +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include newrelic.common.naming.truncateToDNSWithSuffix (dict "name" (include "newrelic.common.naming.fullname" .) suffix "agent-config") }} + namespace: {{ .Release.Namespace }} +data: + newrelic-infra.yml: |- + # This is the configuration file for the infrastructure agent. See: + # https://docs.newrelic.com/docs/infrastructure/install-infrastructure-agent/configuration/infrastructure-agent-configuration-settings/ + {{- include "newrelic.common.agentConfig.defaults" . | nindent 4 }} +``` + + + +## _cluster.tpl +### `newrelic.common.cluster` +Returns the cluster name + +Usage: +```mustache +{{ include "newrelic.common.cluster" . }} +``` + + + +## _custom-attributes.tpl +### `newrelic.common.customAttributes` +Return custom attributes in YAML format. + +Usage: +```mustache +apiVersion: v1 +kind: ConfigMap +metadata: + name: example +data: + custom-attributes.yaml: | + {{- include "newrelic.common.customAttributes" . | nindent 4 }} + custom-attributes.json: | + {{- include "newrelic.common.customAttributes" . | fromYaml | toJson | nindent 4 }} +``` + + + +## _fedramp.tpl +### `newrelic.common.fedramp.enabled` +Returns true if FedRAMP is enabled or an empty string if not. It can be safely used in conditionals as an empty string is a Helm falsiness. + +Usage: +```mustache +{{ include "newrelic.common.fedramp.enabled" . }} +``` + +### `newrelic.common.fedramp.enabled.value` +Returns true if FedRAMP is enabled or false if not. This is to have the value of FedRAMP ready to be templated. + +Usage: +```mustache +{{ include "newrelic.common.fedramp.enabled.value" . }} +``` + + + +## _license.tpl +### `newrelic.common.license.secretName` and ### `newrelic.common.license.secretKeyName` +Returns the secret and key inside the secret where to read the license key. + +The common library will take care of using a user-provided custom secret or creating a secret that contains the license key. + +To create the secret use `newrelic.common.license.secret`. + +Usage: +```mustache +{{- if and (.Values.controlPlane.enabled) (not (include "newrelic.fargate" .)) }} +apiVersion: v1 +kind: Pod +metadata: + name: example +spec: + containers: + - name: agent + env: + - name: "NRIA_LICENSE_KEY" + valueFrom: + secretKeyRef: + name: {{ include "newrelic.common.license.secretName" . }} + key: {{ include "newrelic.common.license.secretKeyName" . }} +``` + + + +## _license_secret.tpl +### `newrelic.common.license.secret` +This function templates the secret that is used by agents and integrations with the license Key provided by the user. It will +template nothing (empty string) if the user provides a custom pair of secret name and key. + +This template also fails in case the user has not provided any license key or custom secret so no safety checks have to be done +by chart writers. + +You just must have a template with these two lines: +```mustache +{{- /* Common library will take care of creating the secret or not. */ -}} +{{- include "newrelic.common.license.secret" . -}} +``` + + + +## _insights.tpl +### `newrelic.common.insightsKey.secretName` and ### `newrelic.common.insightsKey.secretKeyName` +Returns the secret and key inside the secret where to read the insights key. + +The common library will take care of using a user-provided custom secret or creating a secret that contains the insights key. + +To create the secret use `newrelic.common.insightsKey.secret`. + +Usage: +```mustache +apiVersion: v1 +kind: Pod +metadata: + name: statsd +spec: + containers: + - name: statsd + env: + - name: "INSIGHTS_KEY" + valueFrom: + secretKeyRef: + name: {{ include "newrelic.common.insightsKey.secretName" . }} + key: {{ include "newrelic.common.insightsKey.secretKeyName" . }} +``` + + + +## _insights_secret.tpl +### `newrelic.common.insightsKey.secret` +This function templates the secret that is used by agents and integrations with the insights key provided by the user. It will +template nothing (empty string) if the user provides a custom pair of secret name and key. + +This template also fails in case the user has not provided any insights key or custom secret so no safety checks have to be done +by chart writers. + +You just must have a template with these two lines: +```mustache +{{- /* Common library will take care of creating the secret or not. */ -}} +{{- include "newrelic.common.insightsKey.secret" . -}} +``` + + + +## _userkey.tpl +### `newrelic.common.userKey.secretName` and ### `newrelic.common.userKey.secretKeyName` +Returns the secret and key inside the secret where to read a user key. + +The common library will take care of using a user-provided custom secret or creating a secret that contains the insights key. + +To create the secret use `newrelic.common.userKey.secret`. + +Usage: +```mustache +apiVersion: v1 +kind: Pod +metadata: + name: statsd +spec: + containers: + - name: statsd + env: + - name: "API_KEY" + valueFrom: + secretKeyRef: + name: {{ include "newrelic.common.userKey.secretName" . }} + key: {{ include "newrelic.common.userKey.secretKeyName" . }} +``` + + + +## _userkey_secret.tpl +### `newrelic.common.userKey.secret` +This function templates the secret that is used by agents and integrations with a user key provided by the user. It will +template nothing (empty string) if the user provides a custom pair of secret name and key. + +This template also fails in case the user has not provided any API key or custom secret so no safety checks have to be done +by chart writers. + +You just must have a template with these two lines: +```mustache +{{- /* Common library will take care of creating the secret or not. */ -}} +{{- include "newrelic.common.userKey.secret" . -}} +``` + + + +## _region.tpl +### `newrelic.common.region.validate` +Given a string, return a normalized name for the region if valid. + +This function does not need the context of the chart, only the value to be validated. The region returned +honors the region [definition of the newrelic-client-go implementation](https://github.com/newrelic/newrelic-client-go/blob/cbe3e4cf2b95fd37095bf2ffdc5d61cffaec17e2/pkg/region/region_constants.go#L8-L21) +so (as of 2024/09/14) it returns the region as "US", "EU", "Staging", or "Local". + +In case the region provided does not match these 4, the helper calls `fail` and abort the templating. + +Usage: +```mustache +{{ include "newrelic.common.region.validate" "us" }} +``` + +### `newrelic.common.region` +It reads global and local variables for `region`: +```yaml +global: + region: # Note that this can be empty (nil) or "" (empty string) +region: # Note that this can be empty (nil) or "" (empty string) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in your +values a `region` is defined, the global one is going to be always ignored. + +This function gives protection so it enforces users to give the license key as a value in their +`values.yaml` or specify a global or local `region` value. To understand how the `region` value +works, read the documentation of `newrelic.common.region.validate`. + +The function will change the region from US, EU or Staging based of the license key and the +`nrStaging` toggle. Whichever region is computed from the license/toggle can be overridden by +the `region` value. + +Usage: +```mustache +{{ include "newrelic.common.region" . }} +``` + + + +## _low-data-mode.tpl +### `newrelic.common.lowDataMode` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + lowDataMode: # Note that this is empty (nil) +lowDataMode: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `lowdataMode` is defined, the global one is going to be always ignored. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage: +```mustache +{{ include "newrelic.common.lowDataMode" . }} +``` + + + +## _privileged.tpl +### `newrelic.common.privileged` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + privileged: # Note that this is empty (nil) +privileged: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `privileged` is defined, the global one is going to be always ignored. + +Chart writers could override this and put directly a `true` in the `values.yaml` to override the +default of the common library. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage: +```mustache +{{ include "newrelic.common.privileged" . }} +``` + +### `newrelic.common.privileged.value` +Returns true if privileged mode is enabled or false if not. This is to have the value of privileged ready to be templated. + +Usage: +```mustache +{{ include "newrelic.common.privileged.value" . }} +``` + + + +## _proxy.tpl +### `newrelic.common.proxy` +Returns the proxy URL configured by the user. + +Usage: +```mustache +{{ include "newrelic.common.proxy" . }} +``` + + + +## _security-context.tpl +Use these functions to share the security context among all charts. Useful in clusters that have security enforcing not to +use the root user (like OpenShift) or users that have an admission webhooks. + +The functions are: +* `newrelic.common.securityContext.container` +* `newrelic.common.securityContext.pod` + +Usage: +```mustache +apiVersion: v1 +kind: Pod +metadata: + name: example +spec: + spec: + {{- with include "newrelic.common.securityContext.pod" . }} + securityContext: + {{- . | nindent 8 }} + {{- end }} + + containers: + - name: example + {{- with include "nriKubernetes.securityContext.container" . }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} +``` + + + +## _staging.tpl +### `newrelic.common.nrStaging` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + nrStaging: # Note that this is empty (nil) +nrStaging: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `nrStaging` is defined, the global one is going to be always ignored. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage: +```mustache +{{ include "newrelic.common.nrStaging" . }} +``` + +### `newrelic.common.nrStaging.value` +Returns true if staging is enabled or false if not. This is to have the staging value ready to be templated. + +Usage: +```mustache +{{ include "newrelic.common.nrStaging.value" . }} +``` + + + +## _verbose-log.tpl +### `newrelic.common.verboseLog` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + verboseLog: # Note that this is empty (nil) +verboseLog: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `verboseLog` is defined, the global one is going to be always ignored. + +Usage: +```mustache +{{ include "newrelic.common.verboseLog" . }} +``` + +### `newrelic.common.verboseLog.valueAsBoolean` +Returns true if verbose is enabled or false if not. This is to have the verbose value ready to be templated as a boolean + +Usage: +```mustache +{{ include "newrelic.common.verboseLog.valueAsBoolean" . }} +``` + +### `newrelic.common.verboseLog.valueAsInt` +Returns 1 if verbose is enabled or 0 if not. This is to have the verbose value ready to be templated as an integer + +Usage: +```mustache +{{ include "newrelic.common.verboseLog.valueAsInt" . }} +``` diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/README.md b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/README.md new file mode 100644 index 0000000000..10f08ca677 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/README.md @@ -0,0 +1,106 @@ +# Helm Common library + +The common library is a way to unify the UX through all the Helm charts that implement it. + +The tooling suite that New Relic is huge and growing and this allows to set things globally +and locally for a single chart. + +## Documentation for chart writers + +If you are writing a chart that is going to use this library you can check the [developers guide](/library/common-library/DEVELOPERS.md) to see all +the functions/templates that we have implemented, what they do and how to use them. + +## Values managed globally + +We want to have a seamless experience through all the charts so we created this library that tries to standardize the behaviour +of all the charts. Sadly, because of the complexity of all these integrations, not all the charts behave exactly as expected. + +An example is `newrelic-infrastructure` that ignores `hostNetwork` in the control plane scraper because most of the users has the +control plane listening in the node to `localhost`. + +For each chart that has a special behavior (or further information of the behavior) there is a "chart particularities" section +in its README.md that explains which is the expected behavior. + +At the time of writing this, all the charts from `nri-bundle` except `newrelic-logging` and `synthetics-minion` implements this +library and honors global options as described in this document. + +Here is a list of global options: + +| Global keys | Local keys | Default | Merged[1](#values-managed-globally-1) | Description | +|-------------|------------|---------|--------------------------------------------------|-------------| +| global.cluster | cluster | `""` | | Name of the Kubernetes cluster monitored | +| global.licenseKey | licenseKey | `""` | | This set this license key to use | +| global.customSecretName | customSecretName | `""` | | In case you don't want to have the license key in you values, this allows you to point to a user created secret to get the key from there | +| global.customSecretLicenseKey | customSecretLicenseKey | `""` | | In case you don't want to have the license key in you values, this allows you to point to which secret key is the license key located | +| global.podLabels | podLabels | `{}` | yes | Additional labels for chart pods | +| global.labels | labels | `{}` | yes | Additional labels for chart objects | +| global.priorityClassName | priorityClassName | `""` | | Sets pod's priorityClassName | +| global.hostNetwork | hostNetwork | `false` | | Sets pod's hostNetwork | +| global.dnsConfig | dnsConfig | `{}` | | Sets pod's dnsConfig | +| global.images.registry | See [Further information](#values-managed-globally-2) | `""` | | Changes the registry where to get the images. Useful when there is an internal image cache/proxy | +| global.images.pullSecrets | See [Further information](#values-managed-globally-2) | `[]` | yes | Set secrets to be able to fetch images | +| global.podSecurityContext | podSecurityContext | `{}` | | Sets security context (at pod level) | +| global.containerSecurityContext | containerSecurityContext | `{}` | | Sets security context (at container level) | +| global.affinity | affinity | `{}` | | Sets pod/node affinities | +| global.nodeSelector | nodeSelector | `{}` | | Sets pod's node selector | +| global.tolerations | tolerations | `[]` | | Sets pod's tolerations to node taints | +| global.serviceAccount.create | serviceAccount.create | `true` | | Configures if the service account should be created or not | +| global.serviceAccount.name | serviceAccount.name | name of the release | | Change the name of the service account. This is honored if you disable on this cahrt the creation of the service account so you can use your own. | +| global.serviceAccount.annotations | serviceAccount.annotations | `{}` | yes | Add these annotations to the service account we create | +| global.customAttributes | customAttributes | `{}` | | Adds extra attributes to the cluster and all the metrics emitted to the backend | +| global.fedramp | fedramp | `false` | | Enables FedRAMP | +| global.lowDataMode | lowDataMode | `false` | | Reduces number of metrics sent in order to reduce costs | +| global.privileged | privileged | Depends on the chart | | In each integration it has different behavior. See [Further information](#values-managed-globally-3) but all aims to send less metrics to the backend to try to save costs | +| global.proxy | proxy | `""` | | Configures the integration to send all HTTP/HTTPS request through the proxy in that URL. The URL should have a standard format like `https://user:password@hostname:port` | +| global.nrStaging | nrStaging | `false` | | Send the metrics to the staging backend. Requires a valid staging license key | +| global.verboseLog | verboseLog | `false` | | Sets the debug/trace logs to this integration or all integrations if it is set globally | + +### Further information + +#### 1. Merged + +Merged means that the values from global are not replaced by the local ones. Think in this example: +```yaml +global: + labels: + global: global + hostNetwork: true + nodeSelector: + global: global + +labels: + local: local +nodeSelector: + local: local +hostNetwork: false +``` + +This values will template `hostNetwork` to `false`, a map of labels `{ "global": "global", "local": "local" }` and a `nodeSelector` with +`{ "local": "local" }`. + +As Helm by default merges all the maps it could be confusing that we have two behaviors (merging `labels` and replacing `nodeSelector`) +the `values` from global to local. This is the rationale behind this: +* `hostNetwork` is templated to `false` because is overriding the value defined globally. +* `labels` are merged because the user may want to label all the New Relic pods at once and label other solution pods differently for + clarity' sake. +* `nodeSelector` does not merge as `labels` because could make it harder to overwrite/delete a selector that comes from global because + of the logic that Helm follows merging maps. + + +#### 2. Fine grain registries + +Some charts only have 1 image while others that can have 2 or more images. The local path for the registry can change depending +on the chart itself. + +As this is mostly unique per helm chart, you should take a look to the chart's values table (or directly to the `values.yaml` file to see all the +images that you can change. + +This should only be needed if you have an advanced setup that forces you to have granularity enough to force a proxy/cache registry per integration. + + + +#### 3. Privileged mode + +By default, from the common library, the privileged mode is set to false. But most of the helm charts require this to be true to fetch more +metrics so could see a true in some charts. The consequences of the privileged mode differ from one chart to another so for each chart that +honors the privileged mode toggle should be a section in the README explaining which is the behavior with it enabled or disabled. diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_affinity.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_affinity.tpl new file mode 100644 index 0000000000..1b2636754e --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_affinity.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod affinity */ -}} +{{- define "newrelic.common.affinity" -}} + {{- if .Values.affinity -}} + {{- toYaml .Values.affinity -}} + {{- else if .Values.global -}} + {{- if .Values.global.affinity -}} + {{- toYaml .Values.global.affinity -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_agent-config.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_agent-config.tpl new file mode 100644 index 0000000000..9c32861a02 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_agent-config.tpl @@ -0,0 +1,26 @@ +{{/* +This helper should return the defaults that all agents should have +*/}} +{{- define "newrelic.common.agentConfig.defaults" -}} +{{- if include "newrelic.common.verboseLog" . }} +log: + level: trace +{{- end }} + +{{- if (include "newrelic.common.nrStaging" . ) }} +staging: true +{{- end }} + +{{- with include "newrelic.common.proxy" . }} +proxy: {{ . | quote }} +{{- end }} + +{{- with include "newrelic.common.fedramp.enabled" . }} +fedramp: {{ . }} +{{- end }} + +{{- with fromYaml ( include "newrelic.common.customAttributes" . ) }} +custom_attributes: + {{- toYaml . | nindent 2 }} +{{- end }} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_cluster.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_cluster.tpl new file mode 100644 index 0000000000..0197dd35a3 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_cluster.tpl @@ -0,0 +1,15 @@ +{{/* +Return the cluster +*/}} +{{- define "newrelic.common.cluster" -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} + +{{- if .Values.cluster -}} + {{- .Values.cluster -}} +{{- else if $global.cluster -}} + {{- $global.cluster -}} +{{- else -}} + {{ fail "There is not cluster name definition set neither in `.global.cluster' nor `.cluster' in your values.yaml. Cluster name is required." }} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_custom-attributes.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_custom-attributes.tpl new file mode 100644 index 0000000000..92020719c3 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_custom-attributes.tpl @@ -0,0 +1,17 @@ +{{/* +This will render custom attributes as a YAML ready to be templated or be used with `fromYaml`. +*/}} +{{- define "newrelic.common.customAttributes" -}} +{{- $customAttributes := dict -}} + +{{- $global := index .Values "global" | default dict -}} +{{- if $global.customAttributes -}} +{{- $customAttributes = mergeOverwrite $customAttributes $global.customAttributes -}} +{{- end -}} + +{{- if .Values.customAttributes -}} +{{- $customAttributes = mergeOverwrite $customAttributes .Values.customAttributes -}} +{{- end -}} + +{{- toYaml $customAttributes -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_dnsconfig.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_dnsconfig.tpl new file mode 100644 index 0000000000..d4e40aa8af --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_dnsconfig.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod dnsConfig */ -}} +{{- define "newrelic.common.dnsConfig" -}} + {{- if .Values.dnsConfig -}} + {{- toYaml .Values.dnsConfig -}} + {{- else if .Values.global -}} + {{- if .Values.global.dnsConfig -}} + {{- toYaml .Values.global.dnsConfig -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_fedramp.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_fedramp.tpl new file mode 100644 index 0000000000..9df8d6b5e9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_fedramp.tpl @@ -0,0 +1,25 @@ +{{- /* Defines the fedRAMP flag */ -}} +{{- define "newrelic.common.fedramp.enabled" -}} + {{- if .Values.fedramp -}} + {{- if .Values.fedramp.enabled -}} + {{- .Values.fedramp.enabled -}} + {{- end -}} + {{- else if .Values.global -}} + {{- if .Values.global.fedramp -}} + {{- if .Values.global.fedramp.enabled -}} + {{- .Values.global.fedramp.enabled -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} + + + +{{- /* Return FedRAMP value directly ready to be templated */ -}} +{{- define "newrelic.common.fedramp.enabled.value" -}} +{{- if include "newrelic.common.fedramp.enabled" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_hostnetwork.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_hostnetwork.tpl new file mode 100644 index 0000000000..4cf017ef7e --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_hostnetwork.tpl @@ -0,0 +1,39 @@ +{{- /* +Abstraction of the hostNetwork toggle. +This helper allows to override the global `.global.hostNetwork` with the value of `.hostNetwork`. +Returns "true" if `hostNetwork` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.hostNetwork" -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} + +{{- /* +`get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs + +We also want only to return when this is true, returning `false` here will template "false" (string) when doing +an `(include "newrelic.common.hostNetwork" .)`, which is not an "empty string" so it is `true` if it is used +as an evaluation somewhere else. +*/ -}} +{{- if get .Values "hostNetwork" | kindIs "bool" -}} + {{- if .Values.hostNetwork -}} + {{- .Values.hostNetwork -}} + {{- end -}} +{{- else if get $global "hostNetwork" | kindIs "bool" -}} + {{- if $global.hostNetwork -}} + {{- $global.hostNetwork -}} + {{- end -}} +{{- end -}} +{{- end -}} + + +{{- /* +Abstraction of the hostNetwork toggle. +This helper abstracts the function "newrelic.common.hostNetwork" to return true or false directly. +*/ -}} +{{- define "newrelic.common.hostNetwork.value" -}} +{{- if include "newrelic.common.hostNetwork" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_images.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_images.tpl new file mode 100644 index 0000000000..d4fb432905 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_images.tpl @@ -0,0 +1,94 @@ +{{- /* +Return the proper image name +{{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.path.to.the.image "defaultRegistry" "your.private.registry.tld" "context" .) }} +*/ -}} +{{- define "newrelic.common.images.image" -}} + {{- $registryName := include "newrelic.common.images.registry" ( dict "imageRoot" .imageRoot "defaultRegistry" .defaultRegistry "context" .context ) -}} + {{- $repositoryName := include "newrelic.common.images.repository" .imageRoot -}} + {{- $tag := include "newrelic.common.images.tag" ( dict "imageRoot" .imageRoot "context" .context) -}} + + {{- if $registryName -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag | quote -}} + {{- else -}} + {{- printf "%s:%s" $repositoryName $tag | quote -}} + {{- end -}} +{{- end -}} + + + +{{- /* +Return the proper image registry +{{ include "newrelic.common.images.registry" ( dict "imageRoot" .Values.path.to.the.image "defaultRegistry" "your.private.registry.tld" "context" .) }} +*/ -}} +{{- define "newrelic.common.images.registry" -}} +{{- $globalRegistry := "" -}} +{{- if .context.Values.global -}} + {{- if .context.Values.global.images -}} + {{- with .context.Values.global.images.registry -}} + {{- $globalRegistry = . -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- $localRegistry := "" -}} +{{- if .imageRoot.registry -}} + {{- $localRegistry = .imageRoot.registry -}} +{{- end -}} + +{{- $registry := $localRegistry | default $globalRegistry | default .defaultRegistry -}} +{{- if $registry -}} + {{- $registry -}} +{{- end -}} +{{- end -}} + + + +{{- /* +Return the proper image repository +{{ include "newrelic.common.images.repository" .Values.path.to.the.image }} +*/ -}} +{{- define "newrelic.common.images.repository" -}} + {{- .repository -}} +{{- end -}} + + + +{{- /* +Return the proper image tag +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.path.to.the.image "context" .) }} +*/ -}} +{{- define "newrelic.common.images.tag" -}} + {{- .imageRoot.tag | default .context.Chart.AppVersion | toString -}} +{{- end -}} + + + +{{- /* +Return the proper Image Pull Registry Secret Names evaluating values as templates +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" (list .Values.path.to.the.images.pullSecrets1, .Values.path.to.the.images.pullSecrets2) "context" .) }} +*/ -}} +{{- define "newrelic.common.images.renderPullSecrets" -}} + {{- $flatlist := list }} + + {{- if .context.Values.global -}} + {{- if .context.Values.global.images -}} + {{- if .context.Values.global.images.pullSecrets -}} + {{- range .context.Values.global.images.pullSecrets -}} + {{- $flatlist = append $flatlist . -}} + {{- end -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- range .pullSecrets -}} + {{- if not (empty .) -}} + {{- range . -}} + {{- $flatlist = append $flatlist . -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- if $flatlist -}} + {{- toYaml $flatlist -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_insights.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_insights.tpl new file mode 100644 index 0000000000..895c377326 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_insights.tpl @@ -0,0 +1,56 @@ +{{/* +Return the name of the secret holding the Insights Key. +*/}} +{{- define "newrelic.common.insightsKey.secretName" -}} +{{- $default := include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "insightskey" ) -}} +{{- include "newrelic.common.insightsKey._customSecretName" . | default $default -}} +{{- end -}} + +{{/* +Return the name key for the Insights Key inside the secret. +*/}} +{{- define "newrelic.common.insightsKey.secretKeyName" -}} +{{- include "newrelic.common.insightsKey._customSecretKey" . | default "insightsKey" -}} +{{- end -}} + +{{/* +Return local insightsKey if set, global otherwise. +This helper is for internal use. +*/}} +{{- define "newrelic.common.insightsKey._licenseKey" -}} +{{- if .Values.insightsKey -}} + {{- .Values.insightsKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.insightsKey -}} + {{- .Values.global.insightsKey -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name of the secret holding the Insights Key. +This helper is for internal use. +*/}} +{{- define "newrelic.common.insightsKey._customSecretName" -}} +{{- if .Values.customInsightsKeySecretName -}} + {{- .Values.customInsightsKeySecretName -}} +{{- else if .Values.global -}} + {{- if .Values.global.customInsightsKeySecretName -}} + {{- .Values.global.customInsightsKeySecretName -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name key for the Insights Key inside the secret. +This helper is for internal use. +*/}} +{{- define "newrelic.common.insightsKey._customSecretKey" -}} +{{- if .Values.customInsightsKeySecretKey -}} + {{- .Values.customInsightsKeySecretKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.customInsightsKeySecretKey }} + {{- .Values.global.customInsightsKeySecretKey -}} + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_insights_secret.yaml.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_insights_secret.yaml.tpl new file mode 100644 index 0000000000..556caa6ca6 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_insights_secret.yaml.tpl @@ -0,0 +1,21 @@ +{{/* +Renders the insights key secret if user has not specified a custom secret. +*/}} +{{- define "newrelic.common.insightsKey.secret" }} +{{- if not (include "newrelic.common.insightsKey._customSecretName" .) }} +{{- /* Fail if licenseKey is empty and required: */ -}} +{{- if not (include "newrelic.common.insightsKey._licenseKey" .) }} + {{- fail "You must specify a insightsKey or a customInsightsSecretName containing it" }} +{{- end }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "newrelic.common.insightsKey.secretName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +data: + {{ include "newrelic.common.insightsKey.secretKeyName" . }}: {{ include "newrelic.common.insightsKey._licenseKey" . | b64enc }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_labels.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_labels.tpl new file mode 100644 index 0000000000..b025948285 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_labels.tpl @@ -0,0 +1,54 @@ +{{/* +This will render the labels that should be used in all the manifests used by the helm chart. +*/}} +{{- define "newrelic.common.labels" -}} +{{- $global := index .Values "global" | default dict -}} + +{{- $chart := dict "helm.sh/chart" (include "newrelic.common.naming.chart" . ) -}} +{{- $managedBy := dict "app.kubernetes.io/managed-by" .Release.Service -}} +{{- $selectorLabels := fromYaml (include "newrelic.common.labels.selectorLabels" . ) -}} + +{{- $labels := mustMergeOverwrite $chart $managedBy $selectorLabels -}} +{{- if .Chart.AppVersion -}} +{{- $labels = mustMergeOverwrite $labels (dict "app.kubernetes.io/version" .Chart.AppVersion) -}} +{{- end -}} + +{{- $globalUserLabels := $global.labels | default dict -}} +{{- $localUserLabels := .Values.labels | default dict -}} + +{{- $labels = mustMergeOverwrite $labels $globalUserLabels $localUserLabels -}} + +{{- toYaml $labels -}} +{{- end -}} + + + +{{/* +This will render the labels that should be used in deployments/daemonsets template pods as a selector. +*/}} +{{- define "newrelic.common.labels.selectorLabels" -}} +{{- $name := dict "app.kubernetes.io/name" ( include "newrelic.common.naming.name" . ) -}} +{{- $instance := dict "app.kubernetes.io/instance" .Release.Name -}} + +{{- $selectorLabels := mustMergeOverwrite $name $instance -}} + +{{- toYaml $selectorLabels -}} +{{- end }} + + + +{{/* +Pod labels +*/}} +{{- define "newrelic.common.labels.podLabels" -}} +{{- $selectorLabels := fromYaml (include "newrelic.common.labels.selectorLabels" . ) -}} + +{{- $global := index .Values "global" | default dict -}} +{{- $globalPodLabels := $global.podLabels | default dict }} + +{{- $localPodLabels := .Values.podLabels | default dict }} + +{{- $podLabels := mustMergeOverwrite $selectorLabels $globalPodLabels $localPodLabels -}} + +{{- toYaml $podLabels -}} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_license.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_license.tpl new file mode 100644 index 0000000000..cb349f6bb6 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_license.tpl @@ -0,0 +1,68 @@ +{{/* +Return the name of the secret holding the License Key. +*/}} +{{- define "newrelic.common.license.secretName" -}} +{{- $default := include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "license" ) -}} +{{- include "newrelic.common.license._customSecretName" . | default $default -}} +{{- end -}} + +{{/* +Return the name key for the License Key inside the secret. +*/}} +{{- define "newrelic.common.license.secretKeyName" -}} +{{- include "newrelic.common.license._customSecretKey" . | default "licenseKey" -}} +{{- end -}} + +{{/* +Return local licenseKey if set, global otherwise. +This helper is for internal use. +*/}} +{{- define "newrelic.common.license._licenseKey" -}} +{{- if .Values.licenseKey -}} + {{- .Values.licenseKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.licenseKey -}} + {{- .Values.global.licenseKey -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name of the secret holding the License Key. +This helper is for internal use. +*/}} +{{- define "newrelic.common.license._customSecretName" -}} +{{- if .Values.customSecretName -}} + {{- .Values.customSecretName -}} +{{- else if .Values.global -}} + {{- if .Values.global.customSecretName -}} + {{- .Values.global.customSecretName -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name key for the License Key inside the secret. +This helper is for internal use. +*/}} +{{- define "newrelic.common.license._customSecretKey" -}} +{{- if .Values.customSecretLicenseKey -}} + {{- .Values.customSecretLicenseKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.customSecretLicenseKey }} + {{- .Values.global.customSecretLicenseKey -}} + {{- end -}} +{{- end -}} +{{- end -}} + + + +{{/* +Return empty string (falsehood) or "true" if the user set a custom secret for the license. +This helper is for internal use. +*/}} +{{- define "newrelic.common.license._usesCustomSecret" -}} +{{- if or (include "newrelic.common.license._customSecretName" .) (include "newrelic.common.license._customSecretKey" .) -}} +true +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_license_secret.yaml.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_license_secret.yaml.tpl new file mode 100644 index 0000000000..610a0a3370 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_license_secret.yaml.tpl @@ -0,0 +1,21 @@ +{{/* +Renders the license key secret if user has not specified a custom secret. +*/}} +{{- define "newrelic.common.license.secret" }} +{{- if not (include "newrelic.common.license._customSecretName" .) }} +{{- /* Fail if licenseKey is empty and required: */ -}} +{{- if not (include "newrelic.common.license._licenseKey" .) }} + {{- fail "You must specify a licenseKey or a customSecretName containing it" }} +{{- end }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "newrelic.common.license.secretName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +data: + {{ include "newrelic.common.license.secretKeyName" . }}: {{ include "newrelic.common.license._licenseKey" . | b64enc }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_low-data-mode.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_low-data-mode.tpl new file mode 100644 index 0000000000..3dd55ef2ff --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_low-data-mode.tpl @@ -0,0 +1,26 @@ +{{- /* +Abstraction of the lowDataMode toggle. +This helper allows to override the global `.global.lowDataMode` with the value of `.lowDataMode`. +Returns "true" if `lowDataMode` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.lowDataMode" -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if (get .Values "lowDataMode" | kindIs "bool") -}} + {{- if .Values.lowDataMode -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.lowDataMode" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.lowDataMode -}} + {{- end -}} +{{- else -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "lowDataMode" | kindIs "bool" -}} + {{- if $global.lowDataMode -}} + {{- $global.lowDataMode -}} + {{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_naming.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_naming.tpl new file mode 100644 index 0000000000..19fa92648c --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_naming.tpl @@ -0,0 +1,73 @@ +{{/* +This is an function to be called directly with a string just to truncate strings to +63 chars because some Kubernetes name fields are limited to that. +*/}} +{{- define "newrelic.common.naming.truncateToDNS" -}} +{{- . | trunc 63 | trimSuffix "-" }} +{{- end }} + + + +{{- /* +Given a name and a suffix returns a 'DNS Valid' which always include the suffix, truncating the name if needed. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If suffix is too long it gets truncated but it always takes precedence over name, so a 63 chars suffix would suppress the name. +Usage: +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" "" "suffix" "my-suffix" ) }} +*/ -}} +{{- define "newrelic.common.naming.truncateToDNSWithSuffix" -}} +{{- $suffix := (include "newrelic.common.naming.truncateToDNS" .suffix) -}} +{{- $maxLen := (max (sub 63 (add1 (len $suffix))) 0) -}} {{- /* We prepend "-" to the suffix so an additional character is needed */ -}} + +{{- $newName := .name | trunc ($maxLen | int) | trimSuffix "-" -}} +{{- if $newName -}} +{{- printf "%s-%s" $newName $suffix -}} +{{- else -}} +{{ $suffix }} +{{- end -}} + +{{- end -}} + + + +{{/* +Expand the name of the chart. +Uses the Chart name by default if nameOverride is not set. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "newrelic.common.naming.name" -}} +{{- $name := .Values.nameOverride | default .Chart.Name -}} +{{- include "newrelic.common.naming.truncateToDNS" $name -}} +{{- end }} + + + +{{/* +Create a default fully qualified app name. +By default the full name will be "" just in if it has the chart name included in that, if not +it will be concatenated like "-". This could change if fullnameOverride or +nameOverride are set. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "newrelic.common.naming.fullname" -}} +{{- $name := include "newrelic.common.naming.name" . -}} + +{{- if .Values.fullnameOverride -}} + {{- $name = .Values.fullnameOverride -}} +{{- else if not (contains $name .Release.Name) -}} + {{- $name = printf "%s-%s" .Release.Name $name -}} +{{- end -}} + +{{- include "newrelic.common.naming.truncateToDNS" $name -}} + +{{- end -}} + + + +{{/* +Create chart name and version as used by the chart label. +This function should not be used for naming objects. Use "common.naming.{name,fullname}" instead. +*/}} +{{- define "newrelic.common.naming.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_nodeselector.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_nodeselector.tpl new file mode 100644 index 0000000000..d488873412 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_nodeselector.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod nodeSelector */ -}} +{{- define "newrelic.common.nodeSelector" -}} + {{- if .Values.nodeSelector -}} + {{- toYaml .Values.nodeSelector -}} + {{- else if .Values.global -}} + {{- if .Values.global.nodeSelector -}} + {{- toYaml .Values.global.nodeSelector -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_priority-class-name.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_priority-class-name.tpl new file mode 100644 index 0000000000..50182b7343 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_priority-class-name.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the pod priorityClassName */ -}} +{{- define "newrelic.common.priorityClassName" -}} + {{- if .Values.priorityClassName -}} + {{- .Values.priorityClassName -}} + {{- else if .Values.global -}} + {{- if .Values.global.priorityClassName -}} + {{- .Values.global.priorityClassName -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_privileged.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_privileged.tpl new file mode 100644 index 0000000000..f3ae814dd9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_privileged.tpl @@ -0,0 +1,28 @@ +{{- /* +This is a helper that returns whether the chart should assume the user is fine deploying privileged pods. +*/ -}} +{{- define "newrelic.common.privileged" -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists. */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if get .Values "privileged" | kindIs "bool" -}} + {{- if .Values.privileged -}} + {{- .Values.privileged -}} + {{- end -}} +{{- else if get $global "privileged" | kindIs "bool" -}} + {{- if $global.privileged -}} + {{- $global.privileged -}} + {{- end -}} +{{- end -}} +{{- end -}} + + + +{{- /* Return directly "true" or "false" based in the exist of "newrelic.common.privileged" */ -}} +{{- define "newrelic.common.privileged.value" -}} +{{- if include "newrelic.common.privileged" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_proxy.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_proxy.tpl new file mode 100644 index 0000000000..60f34c7ec1 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_proxy.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the proxy */ -}} +{{- define "newrelic.common.proxy" -}} + {{- if .Values.proxy -}} + {{- .Values.proxy -}} + {{- else if .Values.global -}} + {{- if .Values.global.proxy -}} + {{- .Values.global.proxy -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_region.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_region.tpl new file mode 100644 index 0000000000..bdcacf3235 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_region.tpl @@ -0,0 +1,74 @@ +{{/* +Return the region that is being used by the user +*/}} +{{- define "newrelic.common.region" -}} +{{- if and (include "newrelic.common.license._usesCustomSecret" .) (not (include "newrelic.common.region._fromValues" .)) -}} + {{- fail "This Helm Chart is not able to compute the region. You must specify a .global.region or .region if the license is set using a custom secret." -}} +{{- end -}} + +{{- /* Defaults */ -}} +{{- $region := "us" -}} +{{- if include "newrelic.common.nrStaging" . -}} + {{- $region = "staging" -}} +{{- else if include "newrelic.common.region._isEULicenseKey" . -}} + {{- $region = "eu" -}} +{{- end -}} + +{{- include "newrelic.common.region.validate" (include "newrelic.common.region._fromValues" . | default $region ) -}} +{{- end -}} + + + +{{/* +Returns the region from the values if valid. This only return the value from the `values.yaml`. +More intelligence should be used to compute the region. + +Usage: `include "newrelic.common.region.validate" "us"` +*/}} +{{- define "newrelic.common.region.validate" -}} +{{- /* Ref: https://github.com/newrelic/newrelic-client-go/blob/cbe3e4cf2b95fd37095bf2ffdc5d61cffaec17e2/pkg/region/region_constants.go#L8-L21 */ -}} +{{- $region := . | lower -}} +{{- if eq $region "us" -}} + US +{{- else if eq $region "eu" -}} + EU +{{- else if eq $region "staging" -}} + Staging +{{- else if eq $region "local" -}} + Local +{{- else -}} + {{- fail (printf "the region provided is not valid: %s not in \"US\" \"EU\" \"Staging\" \"Local\"" .) -}} +{{- end -}} +{{- end -}} + + + +{{/* +Returns the region from the values. This only return the value from the `values.yaml`. +More intelligence should be used to compute the region. +This helper is for internal use. +*/}} +{{- define "newrelic.common.region._fromValues" -}} +{{- if .Values.region -}} + {{- .Values.region -}} +{{- else if .Values.global -}} + {{- if .Values.global.region -}} + {{- .Values.global.region -}} + {{- end -}} +{{- end -}} +{{- end -}} + + + +{{/* +Return empty string (falsehood) or "true" if the license is for EU region. +This helper is for internal use. +*/}} +{{- define "newrelic.common.region._isEULicenseKey" -}} +{{- if not (include "newrelic.common.license._usesCustomSecret" .) -}} + {{- $license := include "newrelic.common.license._licenseKey" . -}} + {{- if hasPrefix "eu" $license -}} + true + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_security-context.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_security-context.tpl new file mode 100644 index 0000000000..9edfcabfd0 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_security-context.tpl @@ -0,0 +1,23 @@ +{{- /* Defines the container securityContext context */ -}} +{{- define "newrelic.common.securityContext.container" -}} +{{- $global := index .Values "global" | default dict -}} + +{{- if .Values.containerSecurityContext -}} + {{- toYaml .Values.containerSecurityContext -}} +{{- else if $global.containerSecurityContext -}} + {{- toYaml $global.containerSecurityContext -}} +{{- end -}} +{{- end -}} + + + +{{- /* Defines the pod securityContext context */ -}} +{{- define "newrelic.common.securityContext.pod" -}} +{{- $global := index .Values "global" | default dict -}} + +{{- if .Values.podSecurityContext -}} + {{- toYaml .Values.podSecurityContext -}} +{{- else if $global.podSecurityContext -}} + {{- toYaml $global.podSecurityContext -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_serviceaccount.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_serviceaccount.tpl new file mode 100644 index 0000000000..2d352f6ea9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_serviceaccount.tpl @@ -0,0 +1,90 @@ +{{- /* Defines if the service account has to be created or not */ -}} +{{- define "newrelic.common.serviceAccount.create" -}} +{{- $valueFound := false -}} + +{{- /* Look for a global creation of a service account */ -}} +{{- if get .Values "serviceAccount" | kindIs "map" -}} + {{- if (get .Values.serviceAccount "create" | kindIs "bool") -}} + {{- $valueFound = true -}} + {{- if .Values.serviceAccount.create -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.serviceAccount.name" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.serviceAccount.create -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- /* Look for a local creation of a service account */ -}} +{{- if not $valueFound -}} + {{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} + {{- $global := index .Values "global" | default dict -}} + {{- if get $global "serviceAccount" | kindIs "map" -}} + {{- if get $global.serviceAccount "create" | kindIs "bool" -}} + {{- $valueFound = true -}} + {{- if $global.serviceAccount.create -}} + {{- $global.serviceAccount.create -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- /* In case no serviceAccount value has been found, default to "true" */ -}} +{{- if not $valueFound -}} +true +{{- end -}} +{{- end -}} + + + +{{- /* Defines the name of the service account */ -}} +{{- define "newrelic.common.serviceAccount.name" -}} +{{- $localServiceAccount := "" -}} +{{- if get .Values "serviceAccount" | kindIs "map" -}} + {{- if (get .Values.serviceAccount "name" | kindIs "string") -}} + {{- $localServiceAccount = .Values.serviceAccount.name -}} + {{- end -}} +{{- end -}} + +{{- $globalServiceAccount := "" -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "serviceAccount" | kindIs "map" -}} + {{- if get $global.serviceAccount "name" | kindIs "string" -}} + {{- $globalServiceAccount = $global.serviceAccount.name -}} + {{- end -}} +{{- end -}} + +{{- if (include "newrelic.common.serviceAccount.create" .) -}} + {{- $localServiceAccount | default $globalServiceAccount | default (include "newrelic.common.naming.fullname" .) -}} +{{- else -}} + {{- $localServiceAccount | default $globalServiceAccount | default "default" -}} +{{- end -}} +{{- end -}} + + + +{{- /* Merge the global and local annotations for the service account */ -}} +{{- define "newrelic.common.serviceAccount.annotations" -}} +{{- $localServiceAccount := dict -}} +{{- if get .Values "serviceAccount" | kindIs "map" -}} + {{- if get .Values.serviceAccount "annotations" -}} + {{- $localServiceAccount = .Values.serviceAccount.annotations -}} + {{- end -}} +{{- end -}} + +{{- $globalServiceAccount := dict -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "serviceAccount" | kindIs "map" -}} + {{- if get $global.serviceAccount "annotations" -}} + {{- $globalServiceAccount = $global.serviceAccount.annotations -}} + {{- end -}} +{{- end -}} + +{{- $merged := mustMergeOverwrite $globalServiceAccount $localServiceAccount -}} + +{{- if $merged -}} + {{- toYaml $merged -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_staging.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_staging.tpl new file mode 100644 index 0000000000..bd9ad09bb9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_staging.tpl @@ -0,0 +1,39 @@ +{{- /* +Abstraction of the nrStaging toggle. +This helper allows to override the global `.global.nrStaging` with the value of `.nrStaging`. +Returns "true" if `nrStaging` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.nrStaging" -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if (get .Values "nrStaging" | kindIs "bool") -}} + {{- if .Values.nrStaging -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.nrStaging" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.nrStaging -}} + {{- end -}} +{{- else -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "nrStaging" | kindIs "bool" -}} + {{- if $global.nrStaging -}} + {{- $global.nrStaging -}} + {{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} + + + +{{- /* +Returns "true" of "false" directly instead of empty string (Helm falsiness) based on the exit of "newrelic.common.nrStaging" +*/ -}} +{{- define "newrelic.common.nrStaging.value" -}} +{{- if include "newrelic.common.nrStaging" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_tolerations.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_tolerations.tpl new file mode 100644 index 0000000000..e016b38e27 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_tolerations.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod tolerations */ -}} +{{- define "newrelic.common.tolerations" -}} + {{- if .Values.tolerations -}} + {{- toYaml .Values.tolerations -}} + {{- else if .Values.global -}} + {{- if .Values.global.tolerations -}} + {{- toYaml .Values.global.tolerations -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_userkey.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_userkey.tpl new file mode 100644 index 0000000000..982ea8e09d --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_userkey.tpl @@ -0,0 +1,56 @@ +{{/* +Return the name of the secret holding the API Key. +*/}} +{{- define "newrelic.common.userKey.secretName" -}} +{{- $default := include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "userkey" ) -}} +{{- include "newrelic.common.userKey._customSecretName" . | default $default -}} +{{- end -}} + +{{/* +Return the name key for the API Key inside the secret. +*/}} +{{- define "newrelic.common.userKey.secretKeyName" -}} +{{- include "newrelic.common.userKey._customSecretKey" . | default "userKey" -}} +{{- end -}} + +{{/* +Return local API Key if set, global otherwise. +This helper is for internal use. +*/}} +{{- define "newrelic.common.userKey._userKey" -}} +{{- if .Values.userKey -}} + {{- .Values.userKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.userKey -}} + {{- .Values.global.userKey -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name of the secret holding the API Key. +This helper is for internal use. +*/}} +{{- define "newrelic.common.userKey._customSecretName" -}} +{{- if .Values.customUserKeySecretName -}} + {{- .Values.customUserKeySecretName -}} +{{- else if .Values.global -}} + {{- if .Values.global.customUserKeySecretName -}} + {{- .Values.global.customUserKeySecretName -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name key for the API Key inside the secret. +This helper is for internal use. +*/}} +{{- define "newrelic.common.userKey._customSecretKey" -}} +{{- if .Values.customUserKeySecretKey -}} + {{- .Values.customUserKeySecretKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.customUserKeySecretKey }} + {{- .Values.global.customUserKeySecretKey -}} + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_userkey_secret.yaml.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_userkey_secret.yaml.tpl new file mode 100644 index 0000000000..b979856548 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_userkey_secret.yaml.tpl @@ -0,0 +1,21 @@ +{{/* +Renders the user key secret if user has not specified a custom secret. +*/}} +{{- define "newrelic.common.userKey.secret" }} +{{- if not (include "newrelic.common.userKey._customSecretName" .) }} +{{- /* Fail if user key is empty and required: */ -}} +{{- if not (include "newrelic.common.userKey._userKey" .) }} + {{- fail "You must specify a userKey or a customUserKeySecretName containing it" }} +{{- end }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "newrelic.common.userKey.secretName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +data: + {{ include "newrelic.common.userKey.secretKeyName" . }}: {{ include "newrelic.common.userKey._userKey" . | b64enc }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_verbose-log.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_verbose-log.tpl new file mode 100644 index 0000000000..2286d46815 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/templates/_verbose-log.tpl @@ -0,0 +1,54 @@ +{{- /* +Abstraction of the verbose toggle. +This helper allows to override the global `.global.verboseLog` with the value of `.verboseLog`. +Returns "true" if `verbose` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.verboseLog" -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if (get .Values "verboseLog" | kindIs "bool") -}} + {{- if .Values.verboseLog -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.verboseLog" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.verboseLog -}} + {{- end -}} +{{- else -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "verboseLog" | kindIs "bool" -}} + {{- if $global.verboseLog -}} + {{- $global.verboseLog -}} + {{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} + + + +{{- /* +Abstraction of the verbose toggle. +This helper abstracts the function "newrelic.common.verboseLog" to return true or false directly. +*/ -}} +{{- define "newrelic.common.verboseLog.valueAsBoolean" -}} +{{- if include "newrelic.common.verboseLog" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} + + + +{{- /* +Abstraction of the verbose toggle. +This helper abstracts the function "newrelic.common.verboseLog" to return 1 or 0 directly. +*/ -}} +{{- define "newrelic.common.verboseLog.valueAsInt" -}} +{{- if include "newrelic.common.verboseLog" . -}} +1 +{{- else -}} +0 +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/values.yaml new file mode 100644 index 0000000000..75e2d112ad --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/charts/common-library/values.yaml @@ -0,0 +1 @@ +# values are not needed for the library chart, however this file is still needed for helm lint to work. diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/ci/test-values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/ci/test-values.yaml new file mode 100644 index 0000000000..3e154e1d46 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/ci/test-values.yaml @@ -0,0 +1,39 @@ +cluster: test-cluster +licenseKey: pleasePassCIThanks +serviceAccount: + name: newrelic-infra-operator-test +image: + repository: e2e/newrelic-infra-operator + tag: test # Defaults to AppVersion + pullPolicy: IfNotPresent + pullSecrets: + - name: test-pull-secret +admissionWebhooksPatchJob: + volumeMounts: + - name: tmp + mountPath: /tmp + volumes: + - name: tmp + emptyDir: +podAnnotations: + test-annotation: test-value +affinity: + podAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 1 + podAffinityTerm: + topologyKey: topology.kubernetes.io/zone + labelSelector: + matchExpressions: + - key: test-key + operator: In + values: + - test-value +tolerations: +- key: "key1" + operator: "Exists" + effect: "NoSchedule" +nodeSelector: + beta.kubernetes.io/os: linux + +fargate: true diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/NOTES.txt b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/NOTES.txt new file mode 100644 index 0000000000..5b11d2d833 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/NOTES.txt @@ -0,0 +1,4 @@ +Your deployment of the New Relic Infrastructure Operator is complete. +You can check on the progress of this by running the following command: + + kubectl get deployments -o wide -w --namespace {{ .Release.Namespace }} {{ include "newrelic.common.naming.fullname" . }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/_helpers.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/_helpers.tpl new file mode 100644 index 0000000000..8a8858c82b --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/_helpers.tpl @@ -0,0 +1,136 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* +Renders a value that contains template. +Usage: +{{ include "tplvalues.render" ( dict "value" .Values.path.to.the.Value "context" $) }} +*/}} +{{- define "tplvalues.render" -}} + {{- if typeIs "string" .value }} + {{- tpl .value .context }} + {{- else }} + {{- tpl (.value | toYaml) .context }} + {{- end }} +{{- end -}} + +{{- /* +Naming helpers +*/ -}} +{{- define "newrelic-infra-operator.name.admission" -}} +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.name" .) "suffix" "admission") }} +{{- end -}} + +{{- define "newrelic-infra-operator.fullname.admission" -}} +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "admission") }} +{{- end -}} + +{{- define "newrelic-infra-operator.fullname.admission.serviceAccount" -}} +{{- if include "newrelic.common.serviceAccount.create" . -}} + {{- include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "admission") -}} +{{- else -}} + {{- include "newrelic.common.serviceAccount.name" . -}} +{{- end -}} +{{- end -}} + +{{- define "newrelic-infra-operator.name.admission-create" -}} +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.name" .) "suffix" "admission-create") }} +{{- end -}} + +{{- define "newrelic-infra-operator.fullname.admission-create" -}} +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "admission-create") }} +{{- end -}} + +{{- define "newrelic-infra-operator.name.admission-patch" -}} +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.name" .) "suffix" "admission-patch") }} +{{- end -}} + +{{- define "newrelic-infra-operator.fullname.admission-patch" -}} +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "admission-patch") }} +{{- end -}} + +{{- define "newrelic-infra-operator.fullname.self-signed-issuer" -}} +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "self-signed-issuer") }} +{{- end -}} + +{{- define "newrelic-infra-operator.fullname.root-cert" -}} +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "root-cert") }} +{{- end -}} + +{{- define "newrelic-infra-operator.fullname.root-issuer" -}} +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "root-issuer") }} +{{- end -}} + +{{- define "newrelic-infra-operator.fullname.webhook-cert" -}} +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "webhook-cert") }} +{{- end -}} + +{{- define "newrelic-infra-operator.fullname.infra-agent" -}} +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "infra-agent") }} +{{- end -}} + +{{- define "newrelic-infra-operator.fullname.config" -}} +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "config") }} +{{- end -}} + +{{/* +Returns Infra-agent rules +*/}} +{{- define "newrelic-infra-operator.infra-agent-monitoring-rules" -}} +- apiGroups: [""] + resources: + - "nodes" + - "nodes/metrics" + - "nodes/stats" + - "nodes/proxy" + - "pods" + - "services" + - "namespaces" + verbs: ["get", "list"] +- nonResourceURLs: ["/metrics"] + verbs: ["get"] +{{- end -}} + +{{/* +Returns fargate +*/}} +{{- define "newrelic-infra-operator.fargate" -}} +{{- if .Values.global }} + {{- if .Values.global.fargate }} + {{- .Values.global.fargate -}} + {{- end -}} +{{- else if .Values.fargate }} + {{- .Values.fargate -}} +{{- end -}} +{{- end -}} + +{{/* +Returns fargate configuration for configmap data +*/}} +{{- define "newrelic-infra-operator.fargate-config" -}} +infraAgentInjection: + resourcePrefix: {{ include "newrelic.common.naming.fullname" . }} +{{- if include "newrelic-infra-operator.fargate" . }} +{{- if not .Values.config.infraAgentInjection.policies }} + policies: + - podSelector: + matchExpressions: + - key: "eks.amazonaws.com/fargate-profile" + operator: Exists +{{- end }} + agentConfig: +{{- if not .Values.config.infraAgentInjection.agentConfig.customAttributes }} + customAttributes: + - name: computeType + defaultValue: serverless + - name: fargateProfile + fromLabel: eks.amazonaws.com/fargate-profile +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Returns configmap data +*/}} +{{- define "newrelic-infra-operator.configmap.data" -}} +{{ toYaml (merge (include "newrelic-infra-operator.fargate-config" . | fromYaml) .Values.config) }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/clusterrole.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/clusterrole.yaml new file mode 100644 index 0000000000..44c2b3eba8 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/clusterrole.yaml @@ -0,0 +1,27 @@ +{{- if (and (not .Values.customTLSCertificate) (not .Values.certManager.enabled)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "newrelic-infra-operator.fullname.admission" . }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + app: {{ include "newrelic-infra-operator.name.admission" . }} + {{- include "newrelic.common.labels" . | nindent 4 }} +rules: + - apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + verbs: + - get + - update + {{- if .Values.rbac.pspEnabled }} + - apiGroups: ['policy'] + resources: ['podsecuritypolicies'] + verbs: ['use'] + resourceNames: + - {{ include "newrelic-infra-operator.fullname.admission" . }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/clusterrolebinding.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/clusterrolebinding.yaml new file mode 100644 index 0000000000..902206c220 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/clusterrolebinding.yaml @@ -0,0 +1,20 @@ +{{- if (and (not .Values.customTLSCertificate) (not .Values.certManager.enabled)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "newrelic-infra-operator.fullname.admission" . }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + app: {{ include "newrelic-infra-operator.name.admission" . }} + {{- include "newrelic.common.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "newrelic-infra-operator.fullname.admission" . }} +subjects: + - kind: ServiceAccount + name: {{ include "newrelic-infra-operator.fullname.admission.serviceAccount" . }} + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/job-createSecret.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/job-createSecret.yaml new file mode 100644 index 0000000000..022e6254e6 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/job-createSecret.yaml @@ -0,0 +1,57 @@ +{{- if (and (not .Values.customTLSCertificate) (not .Values.certManager.enabled)) }} +apiVersion: batch/v1 +kind: Job +metadata: + namespace: {{ .Release.Namespace }} + name: {{ include "newrelic-infra-operator.fullname.admission-create" . }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + app: {{ include "newrelic-infra-operator.name.admission-create" . }} + {{- include "newrelic.common.labels" . | nindent 4 }} +spec: + template: + metadata: + name: {{ include "newrelic-infra-operator.fullname.admission-create" . }} + labels: + app: {{ include "newrelic-infra-operator.name.admission-create" . }} + {{- include "newrelic.common.labels.podLabels" . | nindent 8 }} + spec: + {{- with include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" ( list .Values.admissionWebhooksPatchJob.image.pullSecrets ) "context" .) }} + imagePullSecrets: + {{- . | nindent 8 }} + {{- end }} + containers: + - name: create + image: {{ include "newrelic.common.images.image" ( dict "defaultRegistry" "registry.k8s.io" "imageRoot" .Values.admissionWebhooksPatchJob.image "context" .) }} + imagePullPolicy: {{ .Values.admissionWebhooksPatchJob.image.pullPolicy }} + args: + - create + - --host={{ include "newrelic.common.naming.fullname" . }},{{ include "newrelic.common.naming.fullname" . }}.{{ .Release.Namespace }}.svc + - --namespace={{ .Release.Namespace }} + - --secret-name={{ include "newrelic-infra-operator.fullname.admission" . }} + - --cert-name=tls.crt + - --key-name=tls.key + {{- if .Values.admissionWebhooksPatchJob.image.volumeMounts }} + volumeMounts: + {{- include "tplvalues.render" ( dict "value" .Values.admissionWebhooksPatchJob.image.volumeMounts "context" $ ) | nindent 10 }} + {{- end }} + {{- if .Values.admissionWebhooksPatchJob.image.volumes }} + volumes: + {{- include "tplvalues.render" ( dict "value" .Values.admissionWebhooksPatchJob.image.volumes "context" $ ) | nindent 8 }} + {{- end }} + restartPolicy: OnFailure + serviceAccountName: {{ include "newrelic-infra-operator.fullname.admission.serviceAccount" . }} + securityContext: + runAsGroup: 2000 + runAsNonRoot: true + runAsUser: 2000 + nodeSelector: + kubernetes.io/os: linux + {{ include "newrelic.common.nodeSelector" . | nindent 8 }} + {{- with include "newrelic.common.tolerations" . }} + tolerations: + {{- . | nindent 8 -}} + {{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/job-patchWebhook.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/job-patchWebhook.yaml new file mode 100644 index 0000000000..61e3636784 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/job-patchWebhook.yaml @@ -0,0 +1,57 @@ +{{- if (and (not .Values.customTLSCertificate) (not .Values.certManager.enabled)) }} +apiVersion: batch/v1 +kind: Job +metadata: + namespace: {{ .Release.Namespace }} + name: {{ include "newrelic-infra-operator.fullname.admission-patch" . }} + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + app: {{ include "newrelic-infra-operator.name.admission-patch" . }} + {{- include "newrelic.common.labels" . | nindent 4 }} +spec: + template: + metadata: + name: {{ include "newrelic-infra-operator.fullname.admission-patch" . }} + labels: + app: {{ include "newrelic-infra-operator.name.admission-patch" . }} + {{- include "newrelic.common.labels" . | nindent 8 }} + spec: + {{- with include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" ( list .Values.admissionWebhooksPatchJob.image.pullSecrets ) "context" .) }} + imagePullSecrets: + {{- . | nindent 8 }} + {{- end }} + containers: + - name: patch + image: {{ include "newrelic.common.images.image" ( dict "defaultRegistry" "registry.k8s.io" "imageRoot" .Values.admissionWebhooksPatchJob.image "context" .) }} + imagePullPolicy: {{ .Values.admissionWebhooksPatchJob.image.pullPolicy }} + args: + - patch + - --webhook-name={{ include "newrelic.common.naming.fullname" . }} + - --namespace={{ .Release.Namespace }} + - --secret-name={{ include "newrelic-infra-operator.fullname.admission" . }} + - --patch-failure-policy=Ignore + - --patch-validating=false + {{- if .Values.admissionWebhooksPatchJob.image.volumeMounts }} + volumeMounts: + {{- include "tplvalues.render" ( dict "value" .Values.admissionWebhooksPatchJob.image.volumeMounts "context" $ ) | nindent 10 }} + {{- end }} + {{- if .Values.admissionWebhooksPatchJob.image.volumes }} + volumes: + {{- include "tplvalues.render" ( dict "value" .Values.admissionWebhooksPatchJob.image.volumes "context" $ ) | nindent 8 }} + {{- end }} + restartPolicy: OnFailure + serviceAccountName: {{ include "newrelic-infra-operator.fullname.admission.serviceAccount" . }} + securityContext: + runAsGroup: 2000 + runAsNonRoot: true + runAsUser: 2000 + nodeSelector: + kubernetes.io/os: linux + {{ include "newrelic.common.nodeSelector" . | nindent 8 }} + {{- with include "newrelic.common.tolerations" . }} + tolerations: + {{- . | nindent 8 -}} + {{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/psp.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/psp.yaml new file mode 100644 index 0000000000..64237abb4d --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/psp.yaml @@ -0,0 +1,50 @@ +{{- if (and (not .Values.customTLSCertificate) (not .Values.certManager.enabled) (.Values.rbac.pspEnabled)) }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ include "newrelic-infra-operator.fullname.admission" . }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + app: {{ include "newrelic-infra-operator.name.admission" . }} + {{- include "newrelic.common.labels" . | nindent 4 }} +spec: + privileged: false + # Required to prevent escalations to root. + # allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + # requiredDropCapabilities: + # - ALL + # Allow core volume types. + volumes: + - 'configMap' + - 'emptyDir' + - 'projected' + - 'secret' + - 'downwardAPI' + - 'persistentVolumeClaim' + hostNetwork: {{ include "newrelic.common.hostNetwork.value" . }} + hostIPC: false + hostPID: false + runAsUser: + # Permits the container to run with root privileges as well. + rule: 'RunAsAny' + seLinux: + # This policy assumes the nodes are using AppArmor rather than SELinux. + rule: 'RunAsAny' + supplementalGroups: + rule: 'MustRunAs' + ranges: + # Forbid adding the root group. + - min: 0 + max: 65535 + fsGroup: + rule: 'MustRunAs' + ranges: + # Forbid adding the root group. + - min: 0 + max: 65535 + readOnlyRootFilesystem: false +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/role.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/role.yaml new file mode 100644 index 0000000000..e3213f7c5e --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/role.yaml @@ -0,0 +1,21 @@ +{{- if (and (not .Values.customTLSCertificate) (not .Values.certManager.enabled)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + namespace: {{ .Release.Namespace }} + name: {{ include "newrelic-infra-operator.fullname.admission" . }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + app: {{ include "newrelic-infra-operator.name.admission" . }} + {{- include "newrelic.common.labels" . | nindent 4 }} +rules: + - apiGroups: + - "" + resources: + - secrets + verbs: + - get + - create +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/rolebinding.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/rolebinding.yaml new file mode 100644 index 0000000000..67eb792987 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/rolebinding.yaml @@ -0,0 +1,21 @@ +{{- if (and (not .Values.customTLSCertificate) (not .Values.certManager.enabled)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + namespace: {{ .Release.Namespace }} + name: {{ include "newrelic-infra-operator.fullname.admission" . }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + app: {{ include "newrelic-infra-operator.name.admission" . }} + {{- include "newrelic.common.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "newrelic-infra-operator.fullname.admission" . }} +subjects: + - kind: ServiceAccount + name: {{ include "newrelic-infra-operator.fullname.admission.serviceAccount" . }} + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/serviceaccount.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/serviceaccount.yaml new file mode 100644 index 0000000000..18eb7347df --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/job-patch/serviceaccount.yaml @@ -0,0 +1,14 @@ +{{- $createServiceAccount := include "newrelic.common.serviceAccount.create" . -}} +{{- if (and $createServiceAccount (not .Values.customTLSCertificate) (not .Values.certManager.enabled)) }} +apiVersion: v1 +kind: ServiceAccount +metadata: + namespace: {{ .Release.Namespace }} + name: {{ include "newrelic-infra-operator.fullname.admission.serviceAccount" . }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + app: {{ include "newrelic-infra-operator.name.admission" . }} + {{- include "newrelic.common.labels" . | nindent 4 }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/mutatingWebhookConfiguration.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/mutatingWebhookConfiguration.yaml new file mode 100644 index 0000000000..efa6052555 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/admission-webhooks/mutatingWebhookConfiguration.yaml @@ -0,0 +1,32 @@ +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + name: {{ include "newrelic.common.naming.fullname" . }} +{{- if .Values.certManager.enabled }} + annotations: + certmanager.k8s.io/inject-ca-from: {{ printf "%s/%s-root-cert" .Release.Namespace (include "newrelic.common.naming.fullname" .) | quote }} + cert-manager.io/inject-ca-from: {{ printf "%s/%s-root-cert" .Release.Namespace (include "newrelic.common.naming.fullname" .) | quote }} +{{- end }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +webhooks: +- name: newrelic-infra-operator.newrelic.com + clientConfig: + service: + name: {{ include "newrelic.common.naming.fullname" . }} + namespace: {{ .Release.Namespace }} + path: "/mutate-v1-pod" +{{- if not .Values.certManager.enabled }} + caBundle: "" +{{- end }} + rules: + - operations: ["CREATE"] + apiGroups: [""] + apiVersions: ["v1"] + resources: ["pods"] + failurePolicy: Ignore + timeoutSeconds: {{ .Values.timeoutSeconds }} + sideEffects: NoneOnDryRun + admissionReviewVersions: + - v1 + reinvocationPolicy: IfNeeded diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/cert-manager.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/cert-manager.yaml new file mode 100644 index 0000000000..800dc24530 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/cert-manager.yaml @@ -0,0 +1,52 @@ +{{ if .Values.certManager.enabled }} +--- +# Create a selfsigned Issuer, in order to create a root CA certificate for +# signing webhook serving certificates +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + namespace: {{ .Release.Namespace }} + name: {{ include "newrelic-infra-operator.fullname.self-signed-issuer" . }} +spec: + selfSigned: {} +--- +# Generate a CA Certificate used to sign certificates for the webhook +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + namespace: {{ .Release.Namespace }} + name: {{ include "newrelic-infra-operator.fullname.root-cert" . }} +spec: + secretName: {{ include "newrelic-infra-operator.fullname.root-cert" . }} + duration: 43800h # 5y + issuerRef: + name: {{ include "newrelic-infra-operator.fullname.self-signed-issuer" . }} + commonName: "ca.webhook.nri" + isCA: true +--- +# Create an Issuer that uses the above generated CA certificate to issue certs +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + namespace: {{ .Release.Namespace }} + name: {{ include "newrelic-infra-operator.fullname.root-issuer" . }} +spec: + ca: + secretName: {{ include "newrelic-infra-operator.fullname.root-cert" . }} +--- +# Finally, generate a serving certificate for the webhook to use +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + namespace: {{ .Release.Namespace }} + name: {{ include "newrelic-infra-operator.fullname.webhook-cert" . }} +spec: + secretName: {{ include "newrelic-infra-operator.fullname.admission" . }} + duration: 8760h # 1y + issuerRef: + name: {{ include "newrelic-infra-operator.fullname.root-issuer" . }} + dnsNames: + - {{ include "newrelic.common.naming.fullname" . }} + - {{ include "newrelic.common.naming.fullname" . }}.{{ .Release.Namespace }} + - {{ include "newrelic.common.naming.fullname" . }}.{{ .Release.Namespace }}.svc +{{ end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/clusterrole.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/clusterrole.yaml new file mode 100644 index 0000000000..cb20e310db --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/clusterrole.yaml @@ -0,0 +1,39 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "newrelic.common.naming.fullname" . }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +rules: + {{/* Allow creating and updating secrets with license key for infra agent. */ -}} + - apiGroups: [""] + resources: + - "secrets" + verbs: ["get", "update", "patch"] + resourceNames: [ {{ include "newrelic-infra-operator.fullname.config" . | quote }} ] + {{/* resourceNames used above do not support "create" verb. */ -}} + - apiGroups: [""] + resources: + - "secrets" + verbs: ["create"] + {{/* "list" and "watch" are required for controller-runtime caching. */ -}} + - apiGroups: ["rbac.authorization.k8s.io"] + resources: ["clusterrolebindings"] + verbs: ["list", "watch", "get"] + {{/* Our controller needs permission to add the ServiceAccounts from the user to the -infra-agent CRB. */ -}} + - apiGroups: ["rbac.authorization.k8s.io"] + resources: ["clusterrolebindings"] + verbs: ["update"] + resourceNames: [ {{ include "newrelic-infra-operator.fullname.infra-agent" . | quote }} ] + {{- /* Controller must have permissions it will grant to other ServiceAccounts. */ -}} + {{- include "newrelic-infra-operator.infra-agent-monitoring-rules" . | nindent 2 }} +--- +{{/* infra-agent is the ClusterRole to be used by the injected agents to get metrics */}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "newrelic-infra-operator.fullname.infra-agent" . }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +rules: + {{- include "newrelic-infra-operator.infra-agent-monitoring-rules" . | nindent 2 }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/clusterrolebinding.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/clusterrolebinding.yaml new file mode 100644 index 0000000000..1f5f8b89ba --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/clusterrolebinding.yaml @@ -0,0 +1,26 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "newrelic.common.naming.fullname" . }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "newrelic.common.naming.fullname" . }} +subjects: + - kind: ServiceAccount + name: {{ template "newrelic.common.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} +--- +{{/* infra-agent is the ClusterRoleBinding to be used by the ServiceAccounts of the injected agents */}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "newrelic-infra-operator.fullname.infra-agent" . }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "newrelic-infra-operator.fullname.infra-agent" . }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/configmap.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/configmap.yaml new file mode 100644 index 0000000000..fdb4a1e3ba --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/configmap.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + namespace: {{ .Release.Namespace }} + name: {{ include "newrelic-infra-operator.fullname.config" . }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +data: + operator.yaml: {{- include "newrelic-infra-operator.configmap.data" . | toYaml | nindent 4 }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/deployment.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/deployment.yaml new file mode 100644 index 0000000000..40f3898873 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/deployment.yaml @@ -0,0 +1,92 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: {{ .Release.Namespace }} + name: {{ include "newrelic.common.naming.fullname" . }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicas }} + selector: + matchLabels: + {{- include "newrelic.common.labels.selectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }} + {{- if .Values.podAnnotations }} + {{- toYaml .Values.podAnnotations | nindent 8 }} + {{- end }} + labels: + {{- include "newrelic.common.labels.podLabels" . | nindent 8 }} + spec: + serviceAccountName: {{ template "newrelic.common.serviceAccount.name" . }} + {{- with include "newrelic.common.securityContext.pod" . }} + securityContext: + {{- . | nindent 8 }} + {{- end }} + {{- with include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" ( list .Values.image.pullSecrets ) "context" .) }} + imagePullSecrets: + {{- . | nindent 8 }} + {{- end }} + containers: + - name: {{ include "newrelic.common.naming.name" . }} + image: {{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.image "context" .) }} + imagePullPolicy: "{{ .Values.image.pullPolicy }}" + {{- with include "newrelic.common.securityContext.container" . }} + securityContext: + {{- . | nindent 10 }} + {{- end }} + env: + - name: CLUSTER_NAME + value: {{ include "newrelic.common.cluster" . }} + - name: NRIA_LICENSE_KEY + valueFrom: + secretKeyRef: + name: {{ include "newrelic.common.license.secretName" . }} + key: {{ include "newrelic.common.license.secretKeyName" . }} + volumeMounts: + - name: config + mountPath: /etc/newrelic/newrelic-infra-operator/ + - name: tls-key-cert-pair + mountPath: /tmp/k8s-webhook-server/serving-certs/ + readinessProbe: + httpGet: + path: /healthz + port: 9440 + initialDelaySeconds: 1 + periodSeconds: 1 + {{- if .Values.resources }} + resources: + {{- toYaml .Values.resources | nindent 10 }} + {{- end }} + volumes: + - name: config + configMap: + name: {{ include "newrelic-infra-operator.fullname.config" . }} + - name: tls-key-cert-pair + secret: + secretName: {{ include "newrelic-infra-operator.fullname.admission" . }} + {{- with include "newrelic.common.priorityClassName" . }} + priorityClassName: {{ . }} + {{- end }} + nodeSelector: + kubernetes.io/os: linux + {{ include "newrelic.common.nodeSelector" . | nindent 8 }} + {{- with include "newrelic.common.tolerations" . }} + tolerations: + {{- . | nindent 8 -}} + {{- end }} + {{- with include "newrelic.common.affinity" . }} + affinity: + {{- . | nindent 8 -}} + {{- end }} + {{- with include "newrelic.common.dnsConfig" . }} + dnsConfig: + {{- . | nindent 8 }} + {{- end }} + hostNetwork: {{ include "newrelic.common.hostNetwork.value" . }} + {{- if include "newrelic.common.hostNetwork" . }} + dnsPolicy: ClusterFirstWithHostNet + {{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/secret.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/secret.yaml new file mode 100644 index 0000000000..f558ee86c9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/secret.yaml @@ -0,0 +1,2 @@ +{{- /* Common library will take care of creating the secret or not. */}} +{{- include "newrelic.common.license.secret" . }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/service.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/service.yaml new file mode 100644 index 0000000000..04af4d09c6 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + namespace: {{ .Release.Namespace }} + name: {{ include "newrelic.common.naming.fullname" . }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +spec: + ports: + - port: 443 + targetPort: 9443 + selector: + {{- include "newrelic.common.labels.selectorLabels" . | nindent 4 }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/serviceaccount.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/serviceaccount.yaml new file mode 100644 index 0000000000..b1e74523e5 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if include "newrelic.common.serviceAccount.create" . -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + {{- if include "newrelic.common.serviceAccount.annotations" . }} + annotations: + {{- include "newrelic.common.serviceAccount.annotations" . | nindent 4 }} + {{- end }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "newrelic.common.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/tests/deployment_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/tests/deployment_test.yaml new file mode 100644 index 0000000000..a1ffa88d05 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/tests/deployment_test.yaml @@ -0,0 +1,32 @@ +suite: test cluster environment variable setup +templates: + - templates/deployment.yaml + - templates/configmap.yaml + - templates/secret.yaml +release: + name: my-release + namespace: my-namespac +tests: + - it: has a linux node selector by default + set: + cluster: my-cluster + licenseKey: use-whatever + asserts: + - equal: + path: spec.template.spec.nodeSelector + value: + kubernetes.io/os: linux + template: templates/deployment.yaml + - it: has a linux node selector and additional selectors + set: + cluster: my-cluster + licenseKey: use-whatever + nodeSelector: + aCoolTestLabel: aCoolTestValue + asserts: + - equal: + path: spec.template.spec.nodeSelector + value: + kubernetes.io/os: linux + aCoolTestLabel: aCoolTestValue + template: templates/deployment.yaml diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/tests/job_patch_psp_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/tests/job_patch_psp_test.yaml new file mode 100644 index 0000000000..78f1b1f6af --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/tests/job_patch_psp_test.yaml @@ -0,0 +1,23 @@ +suite: test rendering for PSPs +templates: + - templates/admission-webhooks/job-patch/psp.yaml +release: + name: my-release + namespace: my-namespace +tests: + - it: If PSPs are enabled PodSecurityPolicy is rendered + set: + cluster: test-cluster + licenseKey: use-whatever + rbac: + pspEnabled: true + asserts: + - hasDocuments: + count: 1 + - it: If PSPs are disabled PodSecurityPolicy isn't rendered + set: + cluster: test-cluster + licenseKey: use-whatever + asserts: + - hasDocuments: + count: 0 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/tests/job_serviceaccount_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/tests/job_serviceaccount_test.yaml new file mode 100644 index 0000000000..c6acda2db4 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/tests/job_serviceaccount_test.yaml @@ -0,0 +1,64 @@ +suite: test job' serviceAccount +templates: + - templates/admission-webhooks/job-patch/job-createSecret.yaml + - templates/admission-webhooks/job-patch/job-patchWebhook.yaml +release: + name: my-release + namespace: my-namespace +tests: + - it: RBAC points to the service account that is created by default + set: + cluster: test-cluster + licenseKey: use-whatever + rbac.create: true + serviceAccount.create: true + asserts: + - equal: + path: spec.template.spec.serviceAccountName + value: my-release-newrelic-infra-operator-admission + + - it: RBAC points to the service account the user supplies when serviceAccount is disabled + set: + cluster: test-cluster + licenseKey: use-whatever + rbac.create: true + serviceAccount.create: false + serviceAccount.name: sa-test + asserts: + - equal: + path: spec.template.spec.serviceAccountName + value: sa-test + + - it: RBAC points to the service account the user supplies when serviceAccount is disabled + set: + cluster: test-cluster + licenseKey: use-whatever + rbac.create: true + serviceAccount.create: false + asserts: + - equal: + path: spec.template.spec.serviceAccountName + value: default + + - it: has a linux node selector by default + set: + cluster: my-cluster + licenseKey: use-whatever + asserts: + - equal: + path: spec.template.spec.nodeSelector + value: + kubernetes.io/os: linux + + - it: has a linux node selector and additional selectors + set: + cluster: my-cluster + licenseKey: use-whatever + nodeSelector: + aCoolTestLabel: aCoolTestValue + asserts: + - equal: + path: spec.template.spec.nodeSelector + value: + kubernetes.io/os: linux + aCoolTestLabel: aCoolTestValue diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/tests/rbac_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/tests/rbac_test.yaml new file mode 100644 index 0000000000..03473cb393 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/tests/rbac_test.yaml @@ -0,0 +1,41 @@ +suite: test RBAC creation +templates: + - templates/admission-webhooks/job-patch/rolebinding.yaml + - templates/admission-webhooks/job-patch/clusterrolebinding.yaml +release: + name: my-release + namespace: my-namespace +tests: + - it: RBAC points to the service account that is created by default + set: + cluster: test-cluster + licenseKey: use-whatever + rbac.create: true + serviceAccount.create: true + asserts: + - equal: + path: subjects[0].name + value: my-release-newrelic-infra-operator-admission + + - it: RBAC points to the service account the user supplies when serviceAccount is disabled + set: + cluster: test-cluster + licenseKey: use-whatever + rbac.create: true + serviceAccount.create: false + serviceAccount.name: sa-test + asserts: + - equal: + path: subjects[0].name + value: sa-test + + - it: RBAC points to the service account the user supplies when serviceAccount is disabled + set: + cluster: test-cluster + licenseKey: use-whatever + rbac.create: true + serviceAccount.create: false + asserts: + - equal: + path: subjects[0].name + value: default diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/values.yaml new file mode 100644 index 0000000000..3dd6fd0554 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infra-operator/values.yaml @@ -0,0 +1,222 @@ +# -- Override the name of the chart +nameOverride: "" +# -- Override the full name of the release +fullnameOverride: "" + +# -- Name of the Kubernetes cluster monitored. Mandatory. Can be configured also with `global.cluster` +cluster: "" +# -- This set this license key to use. Can be configured also with `global.licenseKey` +licenseKey: "" +# -- In case you don't want to have the license key in you values, this allows you to point to a user created secret to get the key from there. Can be configured also with `global.customSecretName` +customSecretName: "" +# -- In case you don't want to have the license key in you values, this allows you to point to which secret key is the license key located. Can be configured also with `global.customSecretLicenseKey` +customSecretLicenseKey: "" + +# -- Image for the New Relic Infrastructure Operator +# @default -- See `values.yaml` +image: + repository: newrelic/newrelic-infra-operator + tag: "" + pullPolicy: IfNotPresent + # -- The secrets that are needed to pull images from a custom registry. + pullSecrets: [] + # - name: regsecret + +# -- Image used to create certificates and inject them to the admission webhook +# @default -- See `values.yaml` +admissionWebhooksPatchJob: + image: + registry: # Defaults to registry.k8s.io + repository: ingress-nginx/kube-webhook-certgen + tag: v1.3.0 + pullPolicy: IfNotPresent + # -- The secrets that are needed to pull images from a custom registry. + pullSecrets: [] + # - name: regsecret + + # -- Volume mounts to add to the job, you might want to mount tmp if Pod Security Policies. + # Enforce a read-only root. + volumeMounts: [] + # - name: tmp + # mountPath: /tmp + # -- Volumes to add to the job container. + volumes: [] + # - name: tmp + # emptyDir: {} + +rbac: + # rbac.pspEnabled -- Whether the chart should create Pod Security Policy objects. + pspEnabled: false + +replicas: 1 + +# -- Resources available for this pod +resources: + limits: + memory: 80M + requests: + cpu: 100m + memory: 30M + +# -- Settings controlling ServiceAccount creation +# @default -- See `values.yaml` +serviceAccount: + # serviceAccount.create -- (bool) Specifies whether a ServiceAccount should be created + # @default -- `true` + create: + # If not set and create is true, a name is generated using the fullname template + name: "" + # Specify any annotations to add to the ServiceAccount + annotations: + +# -- Annotations to add to the pod. +podAnnotations: {} + +# -- Sets pod's priorityClassName. Can be configured also with `global.priorityClassName` +priorityClassName: "" +# -- (bool) Sets pod's hostNetwork. Can be configured also with `global.hostNetwork` +# @default -- `false` +hostNetwork: +# -- Sets pod's dnsConfig. Can be configured also with `global.dnsConfig` +dnsConfig: {} +# -- Sets security context (at pod level). Can be configured also with `global.podSecurityContext` +podSecurityContext: + fsGroup: 1001 + runAsUser: 1001 + runAsGroup: 1001 +# -- Sets security context (at container level). Can be configured also with `global.containerSecurityContext` +containerSecurityContext: {} + +# -- Sets pod/node affinities. Can be configured also with `global.affinity` +affinity: {} +# -- Sets pod's node selector. Can be configured also with `global.nodeSelector` +nodeSelector: {} +# -- Sets pod's tolerations to node taints. Can be configured also with `global.tolerations` +tolerations: [] + +certManager: + # certManager.enabled -- Use cert manager for webhook certs + enabled: false + +# -- Webhook timeout +# Ref: https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#timeouts +timeoutSeconds: 10 + +# -- Operator configuration +# @default -- See `values.yaml` +config: + # -- IgnoreMutationErrors instruments the operator to ignore injection error instead of failing. + # If set to false errors of the injection could block the creation of pods. + ignoreMutationErrors: true + + # -- configuration of the sidecar injection webhook + # @default -- See `values.yaml` + infraAgentInjection: +# policies: +# - podSelector: +# matchExpressions: +# - key: app +# operator: In +# values: [ "nginx-sidecar" ] +# + # All policies are ORed, if one policy matches the sidecar is injected. + # Within a policy PodSelectors, NamespaceSelector and NamespaceName are ANDed, any of these, if not specified, is ignored. + # The following policy is injected if global.fargate=true and matches all pods belonging to any fargate profile. + # policies: + # - podSelector: + # matchExpressions: + # - key: "eks.amazonaws.com/fargate-profile" + # operator: Exists + # Also NamespaceName and NamespaceSelector can be leveraged. + # namespaceName: "my-namespace" + # namespaceSelector: {} + + # -- agentConfig contains the configuration for the container agent injected + # @default -- See `values.yaml` + agentConfig: + # Custom Attributes allows to pass any custom attribute to the injected infra agents. + # The value is computed either from the defaultValue or taken at injected time from Label specified in "fromLabel". + # Either the label should exist or the default should be specified in order to have the injection working. + # customAttributes: + # - name: computeType + # defaultValue: serverless + # - name: fargateProfile + # fromLabel: eks.amazonaws.com/fargate-profile + + # -- Image of the infrastructure agent to be injected. + # @default -- See `values.yaml` + image: + repository: newrelic/infrastructure-k8s + tag: 2.13.15-unprivileged + pullPolicy: IfNotPresent + + # -- configSelectors is the way to configure resource requirements and extra envVars of the injected sidecar container. + # When mutating it will be applied the first configuration having the labelSelector matching with the mutating pod. + # @default -- See `values.yaml` + configSelectors: + - resourceRequirements: # resourceRequirements to apply to the injected sidecar. + limits: + memory: 100M + cpu: 200m + requests: + memory: 50M + cpu: 100m + extraEnvVars: # extraEnvVars to pass to the injected sidecar. + DISABLE_KUBE_STATE_METRICS: "true" + # NRIA_VERBOSE: "1" + labelSelector: + matchExpressions: + - key: "app.kubernetes.io/name" + operator: NotIn + values: ["kube-state-metrics"] + - key: "app" + operator: NotIn + values: ["kube-state-metrics"] + - key: "k8s-app" + operator: NotIn + values: ["kube-state-metrics"] + + - resourceRequirements: + limits: + memory: 300M + cpu: 300m + requests: + memory: 150M + cpu: 150m + labelSelector: + matchLabels: + k8s-app: kube-state-metrics + # extraEnvVars: + # NRIA_VERBOSE: "1" + + - resourceRequirements: + limits: + memory: 300M + cpu: 300m + requests: + memory: 150M + cpu: 150m + labelSelector: + matchLabels: + app: kube-state-metrics + # extraEnvVars: + # NRIA_VERBOSE: "1" + + - resourceRequirements: + limits: + memory: 300M + cpu: 300m + requests: + memory: 150M + cpu: 150m + labelSelector: + matchLabels: + app.kubernetes.io/name: kube-state-metrics + # extraEnvVars: + # NRIA_VERBOSE: "1" + + # pod Security Context of the sidecar injected. + # Notice that ReadOnlyRootFilesystem and AllowPrivilegeEscalation enforced respectively to true and to false. + # podSecurityContext: + # RunAsUser: + # RunAsGroup: diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/.helmignore b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/.helmignore new file mode 100644 index 0000000000..2bfa6a4d95 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/.helmignore @@ -0,0 +1 @@ +tests/ diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/Chart.lock b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/Chart.lock new file mode 100644 index 0000000000..e921cc491d --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: common-library + repository: https://helm-charts.newrelic.com + version: 1.2.0 +digest: sha256:fa87cb007564a39a72739a3e850a91d6b03c0fc27a1115deac042b3ef77b4142 +generated: "2024-06-24T12:11:50.10851808Z" diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/Chart.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/Chart.yaml new file mode 100644 index 0000000000..5d195349f9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/Chart.yaml @@ -0,0 +1,26 @@ +apiVersion: v2 +appVersion: 3.29.4 +dependencies: +- name: common-library + repository: https://helm-charts.newrelic.com + version: 1.2.0 +description: A Helm chart to deploy the New Relic Kubernetes monitoring solution +home: https://docs.newrelic.com/docs/kubernetes-pixie/kubernetes-integration/get-started/introduction-kubernetes-integration/ +icon: https://newrelic.com/themes/custom/curio/assets/mediakit/NR_logo_Horizontal.svg +keywords: +- infrastructure +- newrelic +- monitoring +maintainers: +- name: juanjjaramillo + url: https://github.com/juanjjaramillo +- name: csongnr + url: https://github.com/csongnr +- name: dbudziwojskiNR + url: https://github.com/dbudziwojskiNR +name: newrelic-infrastructure +sources: +- https://github.com/newrelic/nri-kubernetes/ +- https://github.com/newrelic/nri-kubernetes/tree/main/charts/newrelic-infrastructure +- https://github.com/newrelic/infrastructure-agent/ +version: 3.34.4 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/README.md b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/README.md new file mode 100644 index 0000000000..923b6109b1 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/README.md @@ -0,0 +1,220 @@ +# newrelic-infrastructure + +A Helm chart to deploy the New Relic Kubernetes monitoring solution + +**Homepage:** + +# Helm installation + +You can install this chart using [`nri-bundle`](https://github.com/newrelic/helm-charts/tree/master/charts/nri-bundle) located in the +[helm-charts repository](https://github.com/newrelic/helm-charts) or directly from this repository by adding this Helm repository: + +```shell +helm repo add nri-kubernetes https://newrelic.github.io/nri-kubernetes +helm upgrade --install newrelic-infrastructure nri-kubernetes/newrelic-infrastructure -f your-custom-values.yaml +``` + +## Source Code + +* +* +* + +## Values managed globally + +This chart implements the [New Relic's common Helm library](https://github.com/newrelic/helm-charts/tree/master/library/common-library) which +means that it honors a wide range of defaults and globals common to most New Relic Helm charts. + +Options that can be defined globally include `affinity`, `nodeSelector`, `tolerations`, `proxy` and others. The full list can be found at +[user's guide of the common library](https://github.com/newrelic/helm-charts/blob/master/library/common-library/README.md). + +## Chart particularities + +### Low data mode +There are two mechanisms to reduce the amount of data that this integration sends to New Relic. See this snippet from the `values.yaml` file: +```yaml +common: + config: + interval: 15s + +lowDataMode: false +``` + +The `lowDataMode` toggle is the simplest way to reduce data send to Newrelic. Setting it to `true` changes the default scrape interval from 15 seconds +(the default) to 30 seconds. + +If you need for some reason to fine-tune the number of seconds you can use `common.config.interval` directly. If you take a look at the `values.yaml` +file, the value there is `nil`. If any value is set there, the `lowDataMode` toggle is ignored as this value takes precedence. + +Setting this interval above 40 seconds can make you experience issues with the Kubernetes Cluster Explorer so this chart limits setting the interval +inside the range of 10 to 40 seconds. + +### Affinities and tolerations + +The New Relic common library allows to set affinities, tolerations, and node selectors globally using e.g. `.global.affinity` to ease the configuration +when you use this chart using `nri-bundle`. This chart has an extra level of granularity to the components that it deploys: +control plane, ksm, and kubelet. + +Take this snippet as an example: +```yaml +global: + affinity: {} +affinity: {} + +kubelet: + affinity: {} +ksm: + affinity: {} +controlPlane: + affinity: {} +``` + +The order to set an affinity is to set first any `kubelet.affinity`, `ksm.affinity`, or `controlPlane.affinity`. If these values are empty the chart +fallbacks to `affinity` (at root level), and if that value is empty, the chart fallbacks to `global.affinity`. + +The same procedure applies to `nodeSelector` and `tolerations`. + +On the other hand, some components have affinities and tolerations predefined e.g. to be able to run kubelet pods on nodes that are tainted as master +nodes or to schedule the KSM scraper on the same node of KSM to reduce the inter-node traffic. + +If you are having problems assigning pods to nodes it may be because of this. Take a look at the [`values.yaml`](values.yaml) to see if the pod that is +not having your expected behavior has any predefined value. + +### `hostNetwork` toggle + +In versions below v3, changing the `privileged` mode affected the `hostNetwork`. We changed this behavior and now you can set pods to use `hostNetwork` +using the corresponding [flags from the common library](https://github.com/newrelic/helm-charts/blob/master/library/common-library/README.md) +(`.global.hostNetwork` and `.hostNetwork`) but the component that scrapes data from the control plane has always set `hostNetwork` enabled by default +(Look in the [`values.yaml`](values.yaml) for `controlPlane.hostNetwork: true`) + +This is because the most common configuration of the control plane components is to be configured to listen only to `localhost`. + +If your cluster security policy does not allow to use `hostNetwork`, you can disable it control plane monitoring by setting `controlPlane.enabled` to +`false.` + +### `privileged` toggle + +The default value for `privileged` [from the common library](https://github.com/newrelic/helm-charts/blob/master/library/common-library/README.md) is +`false` but in this particular this chart it is set to `true` (Look in the [`values.yaml`](values.yaml) for `privileged: true`) + +This is because when `kubelet` pods need to run in privileged mode to fetch cpu, memory, process, and network metrics of your nodes. + +If your cluster security policy does not allow to have `privileged` in your pod' security context, you can disable it by setting `privileged` to +`false` taking into account that you will lose all the metrics from the host and some metadata from the host that are added to the metrics of the +integrations that you have configured. + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| affinity | object | `{}` | Sets pod/node affinities set almost globally. (See [Affinities and tolerations](README.md#affinities-and-tolerations)) | +| cluster | string | `""` | Name of the Kubernetes cluster monitored. Can be configured also with `global.cluster` | +| common | object | See `values.yaml` | Config that applies to all instances of the solution: kubelet, ksm, control plane and sidecars. | +| common.agentConfig | object | `{}` | Config for the Infrastructure agent. Will be used by the forwarder sidecars and the agent running integrations. See: https://docs.newrelic.com/docs/infrastructure/install-infrastructure-agent/configuration/infrastructure-agent-configuration-settings/ | +| common.config.interval | duration | `15s` (See [Low data mode](README.md#low-data-mode)) | Intervals larger than 40s are not supported and will cause the NR UI to not behave properly. Any non-nil value will override the `lowDataMode` default. | +| common.config.namespaceSelector | object | `{}` | Config for filtering ksm and kubelet metrics by namespace. | +| containerSecurityContext | object | `{}` | Sets security context (at container level). Can be configured also with `global.containerSecurityContext` | +| controlPlane | object | See `values.yaml` | Configuration for the control plane scraper. | +| controlPlane.affinity | object | Deployed only in master nodes. | Affinity for the control plane DaemonSet. | +| controlPlane.agentConfig | object | `{}` | Config for the Infrastructure agent that will forward the metrics to the backend. It will be merged with the configuration in `.common.agentConfig` See: https://docs.newrelic.com/docs/infrastructure/install-infrastructure-agent/configuration/infrastructure-agent-configuration-settings/ | +| controlPlane.config.apiServer | object | Common settings for most K8s distributions. | API Server monitoring configuration | +| controlPlane.config.apiServer.enabled | bool | `true` | Enable API Server monitoring | +| controlPlane.config.controllerManager | object | Common settings for most K8s distributions. | Controller manager monitoring configuration | +| controlPlane.config.controllerManager.enabled | bool | `true` | Enable controller manager monitoring. | +| controlPlane.config.etcd | object | Common settings for most K8s distributions. | etcd monitoring configuration | +| controlPlane.config.etcd.enabled | bool | `true` | Enable etcd monitoring. Might require manual configuration in some environments. | +| controlPlane.config.retries | int | `3` | Number of retries after timeout expired | +| controlPlane.config.scheduler | object | Common settings for most K8s distributions. | Scheduler monitoring configuration | +| controlPlane.config.scheduler.enabled | bool | `true` | Enable scheduler monitoring. | +| controlPlane.config.timeout | string | `"10s"` | Timeout for the Kubernetes APIs contacted by the integration | +| controlPlane.enabled | bool | `true` | Deploy control plane monitoring component. | +| controlPlane.hostNetwork | bool | `true` | Run Control Plane scraper with `hostNetwork`. `hostNetwork` is required for most control plane configurations, as they only accept connections from localhost. | +| controlPlane.kind | string | `"DaemonSet"` | How to deploy the control plane scraper. If autodiscovery is in use, it should be `DaemonSet`. Advanced users using static endpoints set this to `Deployment` to avoid reporting metrics twice. | +| controlPlane.tolerations | list | Schedules in all tainted nodes | Tolerations for the control plane DaemonSet. | +| customAttributes | object | `{}` | Adds extra attributes to the cluster and all the metrics emitted to the backend. Can be configured also with `global.customAttributes` | +| customSecretLicenseKey | string | `""` | In case you don't want to have the license key in you values, this allows you to point to which secret key is the license key located. Can be configured also with `global.customSecretLicenseKey` | +| customSecretName | string | `""` | In case you don't want to have the license key in you values, this allows you to point to a user created secret to get the key from there. Can be configured also with `global.customSecretName` | +| dnsConfig | object | `{}` | Sets pod's dnsConfig. Can be configured also with `global.dnsConfig` | +| enableProcessMetrics | bool | `false` | Collect detailed metrics from processes running in the host. This defaults to true for accounts created before July 20, 2020. ref: https://docs.newrelic.com/docs/release-notes/infrastructure-release-notes/infrastructure-agent-release-notes/new-relic-infrastructure-agent-1120 | +| fedramp.enabled | bool | `false` | Enables FedRAMP. Can be configured also with `global.fedramp.enabled` | +| fullnameOverride | string | `""` | Override the full name of the release | +| hostNetwork | bool | `false` | Sets pod's hostNetwork. Can be configured also with `global.hostNetwork` | +| images | object | See `values.yaml` | Images used by the chart for the integration and agents. | +| images.agent | object | See `values.yaml` | Image for the New Relic Infrastructure Agent plus integrations. | +| images.forwarder | object | See `values.yaml` | Image for the New Relic Infrastructure Agent sidecar. | +| images.integration | object | See `values.yaml` | Image for the New Relic Kubernetes integration. | +| images.pullSecrets | list | `[]` | The secrets that are needed to pull images from a custom registry. | +| integrations | object | `{}` | Config files for other New Relic integrations that should run in this cluster. | +| ksm | object | See `values.yaml` | Configuration for the Deployment that collects state metrics from KSM (kube-state-metrics). | +| ksm.affinity | object | Deployed in the same node as KSM | Affinity for the KSM Deployment. | +| ksm.agentConfig | object | `{}` | Config for the Infrastructure agent that will forward the metrics to the backend. It will be merged with the configuration in `.common.agentConfig` See: https://docs.newrelic.com/docs/infrastructure/install-infrastructure-agent/configuration/infrastructure-agent-configuration-settings/ | +| ksm.config.retries | int | `3` | Number of retries after timeout expired | +| ksm.config.scheme | string | `"http"` | Scheme to use to connect to kube-state-metrics. Supported values are `http` and `https`. | +| ksm.config.selector | string | `"app.kubernetes.io/name=kube-state-metrics"` | Label selector that will be used to automatically discover an instance of kube-state-metrics running in the cluster. | +| ksm.config.timeout | string | `"10s"` | Timeout for the ksm API contacted by the integration | +| ksm.enabled | bool | `true` | Enable cluster state monitoring. Advanced users only. Setting this to `false` is not supported and will break the New Relic experience. | +| ksm.hostNetwork | bool | Not set | Sets pod's hostNetwork. When set bypasses global/common variable | +| ksm.resources | object | 100m/150M -/850M | Resources for the KSM scraper pod. Keep in mind that sharding is not supported at the moment, so memory usage for this component ramps up quickly on large clusters. | +| ksm.tolerations | list | Schedules in all tainted nodes | Tolerations for the KSM Deployment. | +| kubelet | object | See `values.yaml` | Configuration for the DaemonSet that collects metrics from the Kubelet. | +| kubelet.agentConfig | object | `{}` | Config for the Infrastructure agent that will forward the metrics to the backend and will run the integrations in this cluster. It will be merged with the configuration in `.common.agentConfig`. You can see all the agent configurations in [New Relic docs](https://docs.newrelic.com/docs/infrastructure/install-infrastructure-agent/configuration/infrastructure-agent-configuration-settings/) e.g. you can set `passthrough_environment` int the [config file](https://docs.newrelic.com/docs/infrastructure/install-infrastructure-agent/configuration/configure-infrastructure-agent/#config-file) so the agent let use that environment variables to the integrations. | +| kubelet.config.retries | int | `3` | Number of retries after timeout expired | +| kubelet.config.scraperMaxReruns | int | `4` | Max number of scraper rerun when scraper runtime error happens | +| kubelet.config.timeout | string | `"10s"` | Timeout for the kubelet APIs contacted by the integration | +| kubelet.enabled | bool | `true` | Enable kubelet monitoring. Advanced users only. Setting this to `false` is not supported and will break the New Relic experience. | +| kubelet.extraEnv | list | `[]` | Add user environment variables to the agent | +| kubelet.extraEnvFrom | list | `[]` | Add user environment from configMaps or secrets as variables to the agent | +| kubelet.extraVolumeMounts | list | `[]` | Defines where to mount volumes specified with `extraVolumes` | +| kubelet.extraVolumes | list | `[]` | Volumes to mount in the containers | +| kubelet.hostNetwork | bool | Not set | Sets pod's hostNetwork. When set bypasses global/common variable | +| kubelet.tolerations | list | Schedules in all tainted nodes | Tolerations for the control plane DaemonSet. | +| labels | object | `{}` | Additional labels for chart objects. Can be configured also with `global.labels` | +| licenseKey | string | `""` | This set this license key to use. Can be configured also with `global.licenseKey` | +| lowDataMode | bool | `false` (See [Low data mode](README.md#low-data-mode)) | Send less data by incrementing the interval from `15s` (the default when `lowDataMode` is `false` or `nil`) to `30s`. Non-nil values of `common.config.interval` will override this value. | +| nameOverride | string | `""` | Override the name of the chart | +| nodeSelector | object | `{}` | Sets pod's node selector almost globally. (See [Affinities and tolerations](README.md#affinities-and-tolerations)) | +| nrStaging | bool | `false` | Send the metrics to the staging backend. Requires a valid staging license key. Can be configured also with `global.nrStaging` | +| podAnnotations | object | `{}` | Annotations to be added to all pods created by the integration. | +| podLabels | object | `{}` | Additional labels for chart pods. Can be configured also with `global.podLabels` | +| podSecurityContext | object | `{}` | Sets security context (at pod level). Can be configured also with `global.podSecurityContext` | +| priorityClassName | string | `""` | Sets pod's priorityClassName. Can be configured also with `global.priorityClassName` | +| privileged | bool | `true` | Run the integration with full access to the host filesystem and network. Running in this mode allows reporting fine-grained cpu, memory, process and network metrics for your nodes. | +| proxy | string | `""` | Configures the integration to send all HTTP/HTTPS request through the proxy in that URL. The URL should have a standard format like `https://user:password@hostname:port`. Can be configured also with `global.proxy` | +| rbac.create | bool | `true` | Whether the chart should automatically create the RBAC objects required to run. | +| rbac.pspEnabled | bool | `false` | Whether the chart should create Pod Security Policy objects. | +| selfMonitoring.pixie.enabled | bool | `false` | Enables the Pixie Health Check nri-flex config. This Flex config performs periodic checks of the Pixie /healthz and /statusz endpoints exposed by the Pixie Cloud Connector. A status for each endpoint is sent to New Relic in a pixieHealthCheck event. | +| serviceAccount | object | See `values.yaml` | Settings controlling ServiceAccount creation. | +| serviceAccount.create | bool | `true` | Whether the chart should automatically create the ServiceAccount objects required to run. | +| sink.http.probeBackoff | string | `"5s"` | The amount of time the scraper container to backoff when it fails to probe infra agent sidecar. | +| sink.http.probeTimeout | string | `"90s"` | The amount of time the scraper container to probe infra agent sidecar container before giving up and restarting during pod starts. | +| strategy | object | `type: Recreate` | Update strategy for the deployed Deployments. | +| tolerations | list | `[]` | Sets pod's tolerations to node taints almost globally. (See [Affinities and tolerations](README.md#affinities-and-tolerations)) | +| updateStrategy | object | See `values.yaml` | Update strategy for the deployed DaemonSets. | +| verboseLog | bool | `false` | Sets the debug logs to this integration or all integrations if it is set globally. Can be configured also with `global.verboseLog` | + +## Maintainers + +* [juanjjaramillo](https://github.com/juanjjaramillo) +* [csongnr](https://github.com/csongnr) +* [dbudziwojskiNR](https://github.com/dbudziwojskiNR) + +## Past Contributors + +Previous iterations of this chart started as a community project in the [stable Helm chart repository](github.com/helm/charts/). New Relic is very thankful for all the 15+ community members that contributed and helped maintain the chart there over the years: + +* coreypobrien +* sstarcher +* jmccarty3 +* slayerjain +* ryanhope2 +* rk295 +* michaelajr +* isindir +* idirouhab +* ismferd +* enver +* diclophis +* jeffdesc +* costimuraru +* verwilst +* ezelenka diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/README.md.gotmpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/README.md.gotmpl new file mode 100644 index 0000000000..84f2f90834 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/README.md.gotmpl @@ -0,0 +1,137 @@ +{{ template "chart.header" . }} +{{ template "chart.deprecationWarning" . }} + +{{ template "chart.description" . }} + +{{ template "chart.homepageLine" . }} + +# Helm installation + +You can install this chart using [`nri-bundle`](https://github.com/newrelic/helm-charts/tree/master/charts/nri-bundle) located in the +[helm-charts repository](https://github.com/newrelic/helm-charts) or directly from this repository by adding this Helm repository: + +```shell +helm repo add nri-kubernetes https://newrelic.github.io/nri-kubernetes +helm upgrade --install newrelic-infrastructure nri-kubernetes/newrelic-infrastructure -f your-custom-values.yaml +``` + +{{ template "chart.sourcesSection" . }} + +## Values managed globally + +This chart implements the [New Relic's common Helm library](https://github.com/newrelic/helm-charts/tree/master/library/common-library) which +means that it honors a wide range of defaults and globals common to most New Relic Helm charts. + +Options that can be defined globally include `affinity`, `nodeSelector`, `tolerations`, `proxy` and others. The full list can be found at +[user's guide of the common library](https://github.com/newrelic/helm-charts/blob/master/library/common-library/README.md). + +## Chart particularities + +### Low data mode +There are two mechanisms to reduce the amount of data that this integration sends to New Relic. See this snippet from the `values.yaml` file: +```yaml +common: + config: + interval: 15s + +lowDataMode: false +``` + +The `lowDataMode` toggle is the simplest way to reduce data send to Newrelic. Setting it to `true` changes the default scrape interval from 15 seconds +(the default) to 30 seconds. + +If you need for some reason to fine-tune the number of seconds you can use `common.config.interval` directly. If you take a look at the `values.yaml` +file, the value there is `nil`. If any value is set there, the `lowDataMode` toggle is ignored as this value takes precedence. + +Setting this interval above 40 seconds can make you experience issues with the Kubernetes Cluster Explorer so this chart limits setting the interval +inside the range of 10 to 40 seconds. + +### Affinities and tolerations + +The New Relic common library allows to set affinities, tolerations, and node selectors globally using e.g. `.global.affinity` to ease the configuration +when you use this chart using `nri-bundle`. This chart has an extra level of granularity to the components that it deploys: +control plane, ksm, and kubelet. + +Take this snippet as an example: +```yaml +global: + affinity: {} +affinity: {} + +kubelet: + affinity: {} +ksm: + affinity: {} +controlPlane: + affinity: {} +``` + +The order to set an affinity is to set first any `kubelet.affinity`, `ksm.affinity`, or `controlPlane.affinity`. If these values are empty the chart +fallbacks to `affinity` (at root level), and if that value is empty, the chart fallbacks to `global.affinity`. + +The same procedure applies to `nodeSelector` and `tolerations`. + +On the other hand, some components have affinities and tolerations predefined e.g. to be able to run kubelet pods on nodes that are tainted as master +nodes or to schedule the KSM scraper on the same node of KSM to reduce the inter-node traffic. + +If you are having problems assigning pods to nodes it may be because of this. Take a look at the [`values.yaml`](values.yaml) to see if the pod that is +not having your expected behavior has any predefined value. + +### `hostNetwork` toggle + +In versions below v3, changing the `privileged` mode affected the `hostNetwork`. We changed this behavior and now you can set pods to use `hostNetwork` +using the corresponding [flags from the common library](https://github.com/newrelic/helm-charts/blob/master/library/common-library/README.md) +(`.global.hostNetwork` and `.hostNetwork`) but the component that scrapes data from the control plane has always set `hostNetwork` enabled by default +(Look in the [`values.yaml`](values.yaml) for `controlPlane.hostNetwork: true`) + +This is because the most common configuration of the control plane components is to be configured to listen only to `localhost`. + +If your cluster security policy does not allow to use `hostNetwork`, you can disable it control plane monitoring by setting `controlPlane.enabled` to +`false.` + +### `privileged` toggle + +The default value for `privileged` [from the common library](https://github.com/newrelic/helm-charts/blob/master/library/common-library/README.md) is +`false` but in this particular this chart it is set to `true` (Look in the [`values.yaml`](values.yaml) for `privileged: true`) + +This is because when `kubelet` pods need to run in privileged mode to fetch cpu, memory, process, and network metrics of your nodes. + +If your cluster security policy does not allow to have `privileged` in your pod' security context, you can disable it by setting `privileged` to +`false` taking into account that you will lose all the metrics from the host and some metadata from the host that are added to the metrics of the +integrations that you have configured. + +{{ template "chart.valuesSection" . }} + +{{ if .Maintainers }} +## Maintainers +{{ range .Maintainers }} +{{- if .Name }} +{{- if .Url }} +* [{{ .Name }}]({{ .Url }}) +{{- else }} +* {{ .Name }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} + +## Past Contributors + +Previous iterations of this chart started as a community project in the [stable Helm chart repository](github.com/helm/charts/). New Relic is very thankful for all the 15+ community members that contributed and helped maintain the chart there over the years: + +* coreypobrien +* sstarcher +* jmccarty3 +* slayerjain +* ryanhope2 +* rk295 +* michaelajr +* isindir +* idirouhab +* ismferd +* enver +* diclophis +* jeffdesc +* costimuraru +* verwilst +* ezelenka diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/.helmignore b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/.helmignore new file mode 100644 index 0000000000..0e8a0eb36f --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/.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 +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/Chart.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/Chart.yaml new file mode 100644 index 0000000000..b65ac15d4c --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/Chart.yaml @@ -0,0 +1,17 @@ +apiVersion: v2 +description: Provides helpers to provide consistency on all the charts +keywords: +- newrelic +- chart-library +maintainers: +- name: juanjjaramillo + url: https://github.com/juanjjaramillo +- name: csongnr + url: https://github.com/csongnr +- name: dbudziwojskiNR + url: https://github.com/dbudziwojskiNR +- name: kang-makes + url: https://github.com/kang-makes +name: common-library +type: library +version: 1.2.0 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/DEVELOPERS.md b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/DEVELOPERS.md new file mode 100644 index 0000000000..3ccc108e2d --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/DEVELOPERS.md @@ -0,0 +1,663 @@ +# Functions/templates documented for chart writers +Here is some rough documentation separated by the file that contains the function, the function +name and how to use it. We are not covering functions that start with `_` (e.g. +`newrelic.common.license._licenseKey`) because they are used internally by this library for +other helpers. Helm does not have the concept of "public" or "private" functions/templates so +this is a convention of ours. + +## _naming.tpl +These functions are used to name objects. + +### `newrelic.common.naming.name` +This is the same as the idiomatic `CHART-NAME.name` that is created when you use `helm create`. + +It honors `.Values.nameOverride`. + +Usage: +```mustache +{{ include "newrelic.common.naming.name" . }} +``` + +### `newrelic.common.naming.fullname` +This is the same as the idiomatic `CHART-NAME.fullname` that is created when you use `helm create` + +It honors `.Values.fullnameOverride`. + +Usage: +```mustache +{{ include "newrelic.common.naming.fullname" . }} +``` + +### `newrelic.common.naming.chart` +This is the same as the idiomatic `CHART-NAME.chart` that is created when you use `helm create`. + +It is mostly useless for chart writers. It is used internally for templating the labels but there +is no reason to keep it "private". + +Usage: +```mustache +{{ include "newrelic.common.naming.chart" . }} +``` + +### `newrelic.common.naming.truncateToDNS` +This is a useful template that could be used to trim a string to 63 chars and does not end with a dash (`-`). +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). + +Usage: +```mustache +{{ $nameToTruncate := "a-really-really-really-really-REALLY-long-string-that-should-be-truncated-because-it-is-enought-long-to-brak-something" +{{- $truncatedName := include "newrelic.common.naming.truncateToDNS" $nameToTruncate }} +{{- $truncatedName }} +{{- /* This should print: a-really-really-really-really-REALLY-long-string-that-should-be */ -}} +``` + +### `newrelic.common.naming.truncateToDNSWithSuffix` +This template function is the same as the above but instead of receiving a string you should give a `dict` +with a `name` and a `suffix`. This function will join them with a dash (`-`) and trim the `name` so the +result of `name-suffix` is no more than 63 chars + +Usage: +```mustache +{{ $nameToTruncate := "a-really-really-really-really-REALLY-long-string-that-should-be-truncated-because-it-is-enought-long-to-brak-something" +{{- $suffix := "A-NOT-SO-LONG-SUFFIX" }} +{{- $truncatedName := include "truncateToDNSWithSuffix" (dict "name" $nameToTruncate "suffix" $suffix) }} +{{- $truncatedName }} +{{- /* This should print: a-really-really-really-really-REALLY-long-A-NOT-SO-LONG-SUFFIX */ -}} +``` + + + +## _labels.tpl +### `newrelic.common.labels`, `newrelic.common.labels.selectorLabels` and `newrelic.common.labels.podLabels` +These are functions that are used to label objects. They are configured by this `values.yaml` +```yaml +global: + podLabels: {} # included in all the pods of all the charts that implement this library + labels: {} # included in all the objects of all the charts that implement this library +podLabels: {} # included in all the pods of this chart +labels: {} # included in all the objects of this chart +``` + +label maps are merged from global to local values. + +And chart writer should use them like this: +```mustache +metadata: + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +spec: + selector: + matchLabels: + {{- include "newrelic.common.labels.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "newrelic.common.labels.podLabels" . | nindent 8 }} +``` + +`newrelic.common.labels.podLabels` includes `newrelic.common.labels.selectorLabels` automatically. + + + +## _priority-class-name.tpl +### `newrelic.common.priorityClassName` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + priorityClassName: "" +priorityClassName: "" +``` + +Be careful: chart writers should put an empty string (or any kind of Helm falsiness) for this +library to work properly. If in your values a non-falsy `priorityClassName` is found, the global +one is going to be always ignored. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.priorityClassName" . }} + priorityClassName: {{ . }} + {{- end }} +``` + + + +## _hostnetwork.tpl +### `newrelic.common.hostNetwork` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + hostNetwork: # Note that this is empty (nil) +hostNetwork: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `hostNetwork` is defined, the global one is going to be always ignored. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.hostNetwork" . }} + hostNetwork: {{ . }} + {{- end }} +``` + +### `newrelic.common.hostNetwork.value` +This function is an abstraction of the function above but this returns directly "true" or "false". + +Be careful with using this with an `if` as Helm does evaluate "false" (string) as `true`. + +Usage (example in a pod spec): +```mustache +spec: + hostNetwork: {{ include "newrelic.common.hostNetwork.value" . }} +``` + + + +## _dnsconfig.tpl +### `newrelic.common.dnsConfig` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + dnsConfig: {} +dnsConfig: {} +``` + +Be careful: chart writers should put an empty string (or any kind of Helm falsiness) for this +library to work properly. If in your values a non-falsy `dnsConfig` is found, the global +one is going to be always ignored. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.dnsConfig" . }} + dnsConfig: + {{- . | nindent 4 }} + {{- end }} +``` + + + +## _images.tpl +These functions help us to deal with how images are templated. This allows setting `registries` +where to fetch images globally while being flexible enough to fit in different maps of images +and deployments with one or more images. This is the example of a complex `values.yaml` that +we are going to use during the documentation of these functions: + +```yaml +global: + images: + registry: nexus-3-instance.internal.clients-domain.tld +jobImage: + registry: # defaults to "example.tld" when empty in these examples + repository: ingress-nginx/kube-webhook-certgen + tag: v1.1.1 + pullPolicy: IfNotPresent + pullSecrets: [] +images: + integration: + registry: + repository: newrelic/nri-kube-events + tag: 1.8.0 + pullPolicy: IfNotPresent + agent: + registry: + repository: newrelic/k8s-events-forwarder + tag: 1.22.0 + pullPolicy: IfNotPresent + pullSecrets: [] +``` + +### `newrelic.common.images.image` +This will return a string with the image ready to be downloaded that includes the registry, the image and the tag. +`defaultRegistry` is used to keep `registry` field empty in `values.yaml` so you can override the image using +`global.images.registry`, your local `jobImage.registry` and be able to fallback to a registry that is not `docker.io` +(Or the default repository that the client could have set in the CRI). + +Usage: +```mustache +{{- /* For the integration */}} +{{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.images.agent "context" .) }} +{{- /* For jobImage */}} +{{ include "newrelic.common.images.image" ( dict "defaultRegistry" "example.tld" "imageRoot" .Values.jobImage "context" .) }} +``` + +### `newrelic.common.images.registry` +It returns the registry from the global or local values. You should avoid using this helper to create your image +URL and use `newrelic.common.images.image` instead, but it is there to be used in case it is needed. + +Usage: +```mustache +{{- /* For the integration */}} +{{ include "newrelic.common.images.registry" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.registry" ( dict "imageRoot" .Values.images.agent "context" .) }} +{{- /* For jobImage */}} +{{ include "newrelic.common.images.registry" ( dict "defaultRegistry" "example.tld" "imageRoot" .Values.jobImage "context" .) }} +``` + +### `newrelic.common.images.repository` +It returns the image from the values. You should avoid using this helper to create your image +URL and use `newrelic.common.images.image` instead, but it is there to be used in case it is needed. + +Usage: +```mustache +{{- /* For jobImage */}} +{{ include "newrelic.common.images.repository" ( dict "imageRoot" .Values.jobImage "context" .) }} +{{- /* For the integration */}} +{{ include "newrelic.common.images.repository" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.repository" ( dict "imageRoot" .Values.images.agent "context" .) }} +``` + +### `newrelic.common.images.tag` +It returns the image's tag from the values. You should avoid using this helper to create your image +URL and use `newrelic.common.images.image` instead, but it is there to be used in case it is needed. + +Usage: +```mustache +{{- /* For jobImage */}} +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.jobImage "context" .) }} +{{- /* For the integration */}} +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.images.agent "context" .) }} +``` + +### `newrelic.common.images.renderPullSecrets` +If returns a merged map that contains the pull secrets from the global configuration and the local one. + +Usage: +```mustache +{{- /* For jobImage */}} +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" .Values.jobImage.pullSecrets "context" .) }} +{{- /* For the integration */}} +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" .Values.images.pullSecrets "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" .Values.images.pullSecrets "context" .) }} +``` + + + +## _serviceaccount.tpl +These functions are used to evaluate if the service account should be created, with which name and add annotations to it. + +The functions that the common library has implemented for service accounts are: +* `newrelic.common.serviceAccount.create` +* `newrelic.common.serviceAccount.name` +* `newrelic.common.serviceAccount.annotations` + +Usage: +```mustache +{{- if include "newrelic.common.serviceAccount.create" . -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + {{- with (include "newrelic.common.serviceAccount.annotations" .) }} + annotations: + {{- . | nindent 4 }} + {{- end }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "newrelic.common.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} +{{- end }} +``` + + + +## _affinity.tpl, _nodeselector.tpl and _tolerations.tpl +These three files are almost the same and they follow the idiomatic way of `helm create`. + +Each function also looks if there is a global value like the other helpers. +```yaml +global: + affinity: {} + nodeSelector: {} + tolerations: [] +affinity: {} +nodeSelector: {} +tolerations: [] +``` + +The values here are replaced instead of be merged. If a value at root level is found, the global one is ignored. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.nodeSelector" . }} + nodeSelector: + {{- . | nindent 4 }} + {{- end }} + {{- with include "newrelic.common.affinity" . }} + affinity: + {{- . | nindent 4 }} + {{- end }} + {{- with include "newrelic.common.tolerations" . }} + tolerations: + {{- . | nindent 4 }} + {{- end }} +``` + + + +## _agent-config.tpl +### `newrelic.common.agentConfig.defaults` +This returns a YAML that the agent can use directly as a config that includes other options from the values file like verbose mode, +custom attributes, FedRAMP and such. + +Usage: +```mustache +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include newrelic.common.naming.truncateToDNSWithSuffix (dict "name" (include "newrelic.common.naming.fullname" .) suffix "agent-config") }} + namespace: {{ .Release.Namespace }} +data: + newrelic-infra.yml: |- + # This is the configuration file for the infrastructure agent. See: + # https://docs.newrelic.com/docs/infrastructure/install-infrastructure-agent/configuration/infrastructure-agent-configuration-settings/ + {{- include "newrelic.common.agentConfig.defaults" . | nindent 4 }} +``` + + + +## _cluster.tpl +### `newrelic.common.cluster` +Returns the cluster name + +Usage: +```mustache +{{ include "newrelic.common.cluster" . }} +``` + + + +## _custom-attributes.tpl +### `newrelic.common.customAttributes` +Return custom attributes in YAML format. + +Usage: +```mustache +apiVersion: v1 +kind: ConfigMap +metadata: + name: example +data: + custom-attributes.yaml: | + {{- include "newrelic.common.customAttributes" . | nindent 4 }} + custom-attributes.json: | + {{- include "newrelic.common.customAttributes" . | fromYaml | toJson | nindent 4 }} +``` + + + +## _fedramp.tpl +### `newrelic.common.fedramp.enabled` +Returns true if FedRAMP is enabled or an empty string if not. It can be safely used in conditionals as an empty string is a Helm falsiness. + +Usage: +```mustache +{{ include "newrelic.common.fedramp.enabled" . }} +``` + +### `newrelic.common.fedramp.enabled.value` +Returns true if FedRAMP is enabled or false if not. This is to have the value of FedRAMP ready to be templated. + +Usage: +```mustache +{{ include "newrelic.common.fedramp.enabled.value" . }} +``` + + + +## _license.tpl +### `newrelic.common.license.secretName` and ### `newrelic.common.license.secretKeyName` +Returns the secret and key inside the secret where to read the license key. + +The common library will take care of using a user-provided custom secret or creating a secret that contains the license key. + +To create the secret use `newrelic.common.license.secret`. + +Usage: +```mustache +{{- if and (.Values.controlPlane.enabled) (not (include "newrelic.fargate" .)) }} +apiVersion: v1 +kind: Pod +metadata: + name: example +spec: + containers: + - name: agent + env: + - name: "NRIA_LICENSE_KEY" + valueFrom: + secretKeyRef: + name: {{ include "newrelic.common.license.secretName" . }} + key: {{ include "newrelic.common.license.secretKeyName" . }} +``` + + + +## _license_secret.tpl +### `newrelic.common.license.secret` +This function templates the secret that is used by agents and integrations with the license Key provided by the user. It will +template nothing (empty string) if the user provides a custom pair of secret name and key. + +This template also fails in case the user has not provided any license key or custom secret so no safety checks have to be done +by chart writers. + +You just must have a template with these two lines: +```mustache +{{- /* Common library will take care of creating the secret or not. */ -}} +{{- include "newrelic.common.license.secret" . -}} +``` + + + +## _insights.tpl +### `newrelic.common.insightsKey.secretName` and ### `newrelic.common.insightsKey.secretKeyName` +Returns the secret and key inside the secret where to read the insights key. + +The common library will take care of using a user-provided custom secret or creating a secret that contains the insights key. + +To create the secret use `newrelic.common.insightsKey.secret`. + +Usage: +```mustache +apiVersion: v1 +kind: Pod +metadata: + name: statsd +spec: + containers: + - name: statsd + env: + - name: "INSIGHTS_KEY" + valueFrom: + secretKeyRef: + name: {{ include "newrelic.common.insightsKey.secretName" . }} + key: {{ include "newrelic.common.insightsKey.secretKeyName" . }} +``` + + + +## _insights_secret.tpl +### `newrelic.common.insightsKey.secret` +This function templates the secret that is used by agents and integrations with the insights key provided by the user. It will +template nothing (empty string) if the user provides a custom pair of secret name and key. + +This template also fails in case the user has not provided any insights key or custom secret so no safety checks have to be done +by chart writers. + +You just must have a template with these two lines: +```mustache +{{- /* Common library will take care of creating the secret or not. */ -}} +{{- include "newrelic.common.insightsKey.secret" . -}} +``` + + + +## _low-data-mode.tpl +### `newrelic.common.lowDataMode` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + lowDataMode: # Note that this is empty (nil) +lowDataMode: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `lowdataMode` is defined, the global one is going to be always ignored. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage: +```mustache +{{ include "newrelic.common.lowDataMode" . }} +``` + + + +## _privileged.tpl +### `newrelic.common.privileged` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + privileged: # Note that this is empty (nil) +privileged: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `privileged` is defined, the global one is going to be always ignored. + +Chart writers could override this and put directly a `true` in the `values.yaml` to override the +default of the common library. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage: +```mustache +{{ include "newrelic.common.privileged" . }} +``` + +### `newrelic.common.privileged.value` +Returns true if privileged mode is enabled or false if not. This is to have the value of privileged ready to be templated. + +Usage: +```mustache +{{ include "newrelic.common.privileged.value" . }} +``` + + + +## _proxy.tpl +### `newrelic.common.proxy` +Returns the proxy URL configured by the user. + +Usage: +```mustache +{{ include "newrelic.common.proxy" . }} +``` + + + +## _security-context.tpl +Use these functions to share the security context among all charts. Useful in clusters that have security enforcing not to +use the root user (like OpenShift) or users that have an admission webhooks. + +The functions are: +* `newrelic.common.securityContext.container` +* `newrelic.common.securityContext.pod` + +Usage: +```mustache +apiVersion: v1 +kind: Pod +metadata: + name: example +spec: + spec: + {{- with include "newrelic.common.securityContext.pod" . }} + securityContext: + {{- . | nindent 8 }} + {{- end }} + + containers: + - name: example + {{- with include "nriKubernetes.securityContext.container" . }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} +``` + + + +## _staging.tpl +### `newrelic.common.nrStaging` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + nrStaging: # Note that this is empty (nil) +nrStaging: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `nrStaging` is defined, the global one is going to be always ignored. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage: +```mustache +{{ include "newrelic.common.nrStaging" . }} +``` + +### `newrelic.common.nrStaging.value` +Returns true if staging is enabled or false if not. This is to have the staging value ready to be templated. + +Usage: +```mustache +{{ include "newrelic.common.nrStaging.value" . }} +``` + + + +## _verbose-log.tpl +### `newrelic.common.verboseLog` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + verboseLog: # Note that this is empty (nil) +verboseLog: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `verboseLog` is defined, the global one is going to be always ignored. + +Usage: +```mustache +{{ include "newrelic.common.verboseLog" . }} +``` + +### `newrelic.common.verboseLog.valueAsBoolean` +Returns true if verbose is enabled or false if not. This is to have the verbose value ready to be templated as a boolean + +Usage: +```mustache +{{ include "newrelic.common.verboseLog.valueAsBoolean" . }} +``` + +### `newrelic.common.verboseLog.valueAsInt` +Returns 1 if verbose is enabled or 0 if not. This is to have the verbose value ready to be templated as an integer + +Usage: +```mustache +{{ include "newrelic.common.verboseLog.valueAsInt" . }} +``` diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/README.md b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/README.md new file mode 100644 index 0000000000..10f08ca677 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/README.md @@ -0,0 +1,106 @@ +# Helm Common library + +The common library is a way to unify the UX through all the Helm charts that implement it. + +The tooling suite that New Relic is huge and growing and this allows to set things globally +and locally for a single chart. + +## Documentation for chart writers + +If you are writing a chart that is going to use this library you can check the [developers guide](/library/common-library/DEVELOPERS.md) to see all +the functions/templates that we have implemented, what they do and how to use them. + +## Values managed globally + +We want to have a seamless experience through all the charts so we created this library that tries to standardize the behaviour +of all the charts. Sadly, because of the complexity of all these integrations, not all the charts behave exactly as expected. + +An example is `newrelic-infrastructure` that ignores `hostNetwork` in the control plane scraper because most of the users has the +control plane listening in the node to `localhost`. + +For each chart that has a special behavior (or further information of the behavior) there is a "chart particularities" section +in its README.md that explains which is the expected behavior. + +At the time of writing this, all the charts from `nri-bundle` except `newrelic-logging` and `synthetics-minion` implements this +library and honors global options as described in this document. + +Here is a list of global options: + +| Global keys | Local keys | Default | Merged[1](#values-managed-globally-1) | Description | +|-------------|------------|---------|--------------------------------------------------|-------------| +| global.cluster | cluster | `""` | | Name of the Kubernetes cluster monitored | +| global.licenseKey | licenseKey | `""` | | This set this license key to use | +| global.customSecretName | customSecretName | `""` | | In case you don't want to have the license key in you values, this allows you to point to a user created secret to get the key from there | +| global.customSecretLicenseKey | customSecretLicenseKey | `""` | | In case you don't want to have the license key in you values, this allows you to point to which secret key is the license key located | +| global.podLabels | podLabels | `{}` | yes | Additional labels for chart pods | +| global.labels | labels | `{}` | yes | Additional labels for chart objects | +| global.priorityClassName | priorityClassName | `""` | | Sets pod's priorityClassName | +| global.hostNetwork | hostNetwork | `false` | | Sets pod's hostNetwork | +| global.dnsConfig | dnsConfig | `{}` | | Sets pod's dnsConfig | +| global.images.registry | See [Further information](#values-managed-globally-2) | `""` | | Changes the registry where to get the images. Useful when there is an internal image cache/proxy | +| global.images.pullSecrets | See [Further information](#values-managed-globally-2) | `[]` | yes | Set secrets to be able to fetch images | +| global.podSecurityContext | podSecurityContext | `{}` | | Sets security context (at pod level) | +| global.containerSecurityContext | containerSecurityContext | `{}` | | Sets security context (at container level) | +| global.affinity | affinity | `{}` | | Sets pod/node affinities | +| global.nodeSelector | nodeSelector | `{}` | | Sets pod's node selector | +| global.tolerations | tolerations | `[]` | | Sets pod's tolerations to node taints | +| global.serviceAccount.create | serviceAccount.create | `true` | | Configures if the service account should be created or not | +| global.serviceAccount.name | serviceAccount.name | name of the release | | Change the name of the service account. This is honored if you disable on this cahrt the creation of the service account so you can use your own. | +| global.serviceAccount.annotations | serviceAccount.annotations | `{}` | yes | Add these annotations to the service account we create | +| global.customAttributes | customAttributes | `{}` | | Adds extra attributes to the cluster and all the metrics emitted to the backend | +| global.fedramp | fedramp | `false` | | Enables FedRAMP | +| global.lowDataMode | lowDataMode | `false` | | Reduces number of metrics sent in order to reduce costs | +| global.privileged | privileged | Depends on the chart | | In each integration it has different behavior. See [Further information](#values-managed-globally-3) but all aims to send less metrics to the backend to try to save costs | +| global.proxy | proxy | `""` | | Configures the integration to send all HTTP/HTTPS request through the proxy in that URL. The URL should have a standard format like `https://user:password@hostname:port` | +| global.nrStaging | nrStaging | `false` | | Send the metrics to the staging backend. Requires a valid staging license key | +| global.verboseLog | verboseLog | `false` | | Sets the debug/trace logs to this integration or all integrations if it is set globally | + +### Further information + +#### 1. Merged + +Merged means that the values from global are not replaced by the local ones. Think in this example: +```yaml +global: + labels: + global: global + hostNetwork: true + nodeSelector: + global: global + +labels: + local: local +nodeSelector: + local: local +hostNetwork: false +``` + +This values will template `hostNetwork` to `false`, a map of labels `{ "global": "global", "local": "local" }` and a `nodeSelector` with +`{ "local": "local" }`. + +As Helm by default merges all the maps it could be confusing that we have two behaviors (merging `labels` and replacing `nodeSelector`) +the `values` from global to local. This is the rationale behind this: +* `hostNetwork` is templated to `false` because is overriding the value defined globally. +* `labels` are merged because the user may want to label all the New Relic pods at once and label other solution pods differently for + clarity' sake. +* `nodeSelector` does not merge as `labels` because could make it harder to overwrite/delete a selector that comes from global because + of the logic that Helm follows merging maps. + + +#### 2. Fine grain registries + +Some charts only have 1 image while others that can have 2 or more images. The local path for the registry can change depending +on the chart itself. + +As this is mostly unique per helm chart, you should take a look to the chart's values table (or directly to the `values.yaml` file to see all the +images that you can change. + +This should only be needed if you have an advanced setup that forces you to have granularity enough to force a proxy/cache registry per integration. + + + +#### 3. Privileged mode + +By default, from the common library, the privileged mode is set to false. But most of the helm charts require this to be true to fetch more +metrics so could see a true in some charts. The consequences of the privileged mode differ from one chart to another so for each chart that +honors the privileged mode toggle should be a section in the README explaining which is the behavior with it enabled or disabled. diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_affinity.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_affinity.tpl new file mode 100644 index 0000000000..1b2636754e --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_affinity.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod affinity */ -}} +{{- define "newrelic.common.affinity" -}} + {{- if .Values.affinity -}} + {{- toYaml .Values.affinity -}} + {{- else if .Values.global -}} + {{- if .Values.global.affinity -}} + {{- toYaml .Values.global.affinity -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_agent-config.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_agent-config.tpl new file mode 100644 index 0000000000..9c32861a02 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_agent-config.tpl @@ -0,0 +1,26 @@ +{{/* +This helper should return the defaults that all agents should have +*/}} +{{- define "newrelic.common.agentConfig.defaults" -}} +{{- if include "newrelic.common.verboseLog" . }} +log: + level: trace +{{- end }} + +{{- if (include "newrelic.common.nrStaging" . ) }} +staging: true +{{- end }} + +{{- with include "newrelic.common.proxy" . }} +proxy: {{ . | quote }} +{{- end }} + +{{- with include "newrelic.common.fedramp.enabled" . }} +fedramp: {{ . }} +{{- end }} + +{{- with fromYaml ( include "newrelic.common.customAttributes" . ) }} +custom_attributes: + {{- toYaml . | nindent 2 }} +{{- end }} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_cluster.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_cluster.tpl new file mode 100644 index 0000000000..0197dd35a3 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_cluster.tpl @@ -0,0 +1,15 @@ +{{/* +Return the cluster +*/}} +{{- define "newrelic.common.cluster" -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} + +{{- if .Values.cluster -}} + {{- .Values.cluster -}} +{{- else if $global.cluster -}} + {{- $global.cluster -}} +{{- else -}} + {{ fail "There is not cluster name definition set neither in `.global.cluster' nor `.cluster' in your values.yaml. Cluster name is required." }} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_custom-attributes.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_custom-attributes.tpl new file mode 100644 index 0000000000..92020719c3 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_custom-attributes.tpl @@ -0,0 +1,17 @@ +{{/* +This will render custom attributes as a YAML ready to be templated or be used with `fromYaml`. +*/}} +{{- define "newrelic.common.customAttributes" -}} +{{- $customAttributes := dict -}} + +{{- $global := index .Values "global" | default dict -}} +{{- if $global.customAttributes -}} +{{- $customAttributes = mergeOverwrite $customAttributes $global.customAttributes -}} +{{- end -}} + +{{- if .Values.customAttributes -}} +{{- $customAttributes = mergeOverwrite $customAttributes .Values.customAttributes -}} +{{- end -}} + +{{- toYaml $customAttributes -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_dnsconfig.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_dnsconfig.tpl new file mode 100644 index 0000000000..d4e40aa8af --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_dnsconfig.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod dnsConfig */ -}} +{{- define "newrelic.common.dnsConfig" -}} + {{- if .Values.dnsConfig -}} + {{- toYaml .Values.dnsConfig -}} + {{- else if .Values.global -}} + {{- if .Values.global.dnsConfig -}} + {{- toYaml .Values.global.dnsConfig -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_fedramp.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_fedramp.tpl new file mode 100644 index 0000000000..9df8d6b5e9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_fedramp.tpl @@ -0,0 +1,25 @@ +{{- /* Defines the fedRAMP flag */ -}} +{{- define "newrelic.common.fedramp.enabled" -}} + {{- if .Values.fedramp -}} + {{- if .Values.fedramp.enabled -}} + {{- .Values.fedramp.enabled -}} + {{- end -}} + {{- else if .Values.global -}} + {{- if .Values.global.fedramp -}} + {{- if .Values.global.fedramp.enabled -}} + {{- .Values.global.fedramp.enabled -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} + + + +{{- /* Return FedRAMP value directly ready to be templated */ -}} +{{- define "newrelic.common.fedramp.enabled.value" -}} +{{- if include "newrelic.common.fedramp.enabled" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_hostnetwork.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_hostnetwork.tpl new file mode 100644 index 0000000000..4cf017ef7e --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_hostnetwork.tpl @@ -0,0 +1,39 @@ +{{- /* +Abstraction of the hostNetwork toggle. +This helper allows to override the global `.global.hostNetwork` with the value of `.hostNetwork`. +Returns "true" if `hostNetwork` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.hostNetwork" -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} + +{{- /* +`get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs + +We also want only to return when this is true, returning `false` here will template "false" (string) when doing +an `(include "newrelic.common.hostNetwork" .)`, which is not an "empty string" so it is `true` if it is used +as an evaluation somewhere else. +*/ -}} +{{- if get .Values "hostNetwork" | kindIs "bool" -}} + {{- if .Values.hostNetwork -}} + {{- .Values.hostNetwork -}} + {{- end -}} +{{- else if get $global "hostNetwork" | kindIs "bool" -}} + {{- if $global.hostNetwork -}} + {{- $global.hostNetwork -}} + {{- end -}} +{{- end -}} +{{- end -}} + + +{{- /* +Abstraction of the hostNetwork toggle. +This helper abstracts the function "newrelic.common.hostNetwork" to return true or false directly. +*/ -}} +{{- define "newrelic.common.hostNetwork.value" -}} +{{- if include "newrelic.common.hostNetwork" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_images.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_images.tpl new file mode 100644 index 0000000000..d4fb432905 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_images.tpl @@ -0,0 +1,94 @@ +{{- /* +Return the proper image name +{{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.path.to.the.image "defaultRegistry" "your.private.registry.tld" "context" .) }} +*/ -}} +{{- define "newrelic.common.images.image" -}} + {{- $registryName := include "newrelic.common.images.registry" ( dict "imageRoot" .imageRoot "defaultRegistry" .defaultRegistry "context" .context ) -}} + {{- $repositoryName := include "newrelic.common.images.repository" .imageRoot -}} + {{- $tag := include "newrelic.common.images.tag" ( dict "imageRoot" .imageRoot "context" .context) -}} + + {{- if $registryName -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag | quote -}} + {{- else -}} + {{- printf "%s:%s" $repositoryName $tag | quote -}} + {{- end -}} +{{- end -}} + + + +{{- /* +Return the proper image registry +{{ include "newrelic.common.images.registry" ( dict "imageRoot" .Values.path.to.the.image "defaultRegistry" "your.private.registry.tld" "context" .) }} +*/ -}} +{{- define "newrelic.common.images.registry" -}} +{{- $globalRegistry := "" -}} +{{- if .context.Values.global -}} + {{- if .context.Values.global.images -}} + {{- with .context.Values.global.images.registry -}} + {{- $globalRegistry = . -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- $localRegistry := "" -}} +{{- if .imageRoot.registry -}} + {{- $localRegistry = .imageRoot.registry -}} +{{- end -}} + +{{- $registry := $localRegistry | default $globalRegistry | default .defaultRegistry -}} +{{- if $registry -}} + {{- $registry -}} +{{- end -}} +{{- end -}} + + + +{{- /* +Return the proper image repository +{{ include "newrelic.common.images.repository" .Values.path.to.the.image }} +*/ -}} +{{- define "newrelic.common.images.repository" -}} + {{- .repository -}} +{{- end -}} + + + +{{- /* +Return the proper image tag +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.path.to.the.image "context" .) }} +*/ -}} +{{- define "newrelic.common.images.tag" -}} + {{- .imageRoot.tag | default .context.Chart.AppVersion | toString -}} +{{- end -}} + + + +{{- /* +Return the proper Image Pull Registry Secret Names evaluating values as templates +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" (list .Values.path.to.the.images.pullSecrets1, .Values.path.to.the.images.pullSecrets2) "context" .) }} +*/ -}} +{{- define "newrelic.common.images.renderPullSecrets" -}} + {{- $flatlist := list }} + + {{- if .context.Values.global -}} + {{- if .context.Values.global.images -}} + {{- if .context.Values.global.images.pullSecrets -}} + {{- range .context.Values.global.images.pullSecrets -}} + {{- $flatlist = append $flatlist . -}} + {{- end -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- range .pullSecrets -}} + {{- if not (empty .) -}} + {{- range . -}} + {{- $flatlist = append $flatlist . -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- if $flatlist -}} + {{- toYaml $flatlist -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_insights.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_insights.tpl new file mode 100644 index 0000000000..895c377326 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_insights.tpl @@ -0,0 +1,56 @@ +{{/* +Return the name of the secret holding the Insights Key. +*/}} +{{- define "newrelic.common.insightsKey.secretName" -}} +{{- $default := include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "insightskey" ) -}} +{{- include "newrelic.common.insightsKey._customSecretName" . | default $default -}} +{{- end -}} + +{{/* +Return the name key for the Insights Key inside the secret. +*/}} +{{- define "newrelic.common.insightsKey.secretKeyName" -}} +{{- include "newrelic.common.insightsKey._customSecretKey" . | default "insightsKey" -}} +{{- end -}} + +{{/* +Return local insightsKey if set, global otherwise. +This helper is for internal use. +*/}} +{{- define "newrelic.common.insightsKey._licenseKey" -}} +{{- if .Values.insightsKey -}} + {{- .Values.insightsKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.insightsKey -}} + {{- .Values.global.insightsKey -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name of the secret holding the Insights Key. +This helper is for internal use. +*/}} +{{- define "newrelic.common.insightsKey._customSecretName" -}} +{{- if .Values.customInsightsKeySecretName -}} + {{- .Values.customInsightsKeySecretName -}} +{{- else if .Values.global -}} + {{- if .Values.global.customInsightsKeySecretName -}} + {{- .Values.global.customInsightsKeySecretName -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name key for the Insights Key inside the secret. +This helper is for internal use. +*/}} +{{- define "newrelic.common.insightsKey._customSecretKey" -}} +{{- if .Values.customInsightsKeySecretKey -}} + {{- .Values.customInsightsKeySecretKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.customInsightsKeySecretKey }} + {{- .Values.global.customInsightsKeySecretKey -}} + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_insights_secret.yaml.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_insights_secret.yaml.tpl new file mode 100644 index 0000000000..556caa6ca6 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_insights_secret.yaml.tpl @@ -0,0 +1,21 @@ +{{/* +Renders the insights key secret if user has not specified a custom secret. +*/}} +{{- define "newrelic.common.insightsKey.secret" }} +{{- if not (include "newrelic.common.insightsKey._customSecretName" .) }} +{{- /* Fail if licenseKey is empty and required: */ -}} +{{- if not (include "newrelic.common.insightsKey._licenseKey" .) }} + {{- fail "You must specify a insightsKey or a customInsightsSecretName containing it" }} +{{- end }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "newrelic.common.insightsKey.secretName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +data: + {{ include "newrelic.common.insightsKey.secretKeyName" . }}: {{ include "newrelic.common.insightsKey._licenseKey" . | b64enc }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_labels.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_labels.tpl new file mode 100644 index 0000000000..b025948285 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_labels.tpl @@ -0,0 +1,54 @@ +{{/* +This will render the labels that should be used in all the manifests used by the helm chart. +*/}} +{{- define "newrelic.common.labels" -}} +{{- $global := index .Values "global" | default dict -}} + +{{- $chart := dict "helm.sh/chart" (include "newrelic.common.naming.chart" . ) -}} +{{- $managedBy := dict "app.kubernetes.io/managed-by" .Release.Service -}} +{{- $selectorLabels := fromYaml (include "newrelic.common.labels.selectorLabels" . ) -}} + +{{- $labels := mustMergeOverwrite $chart $managedBy $selectorLabels -}} +{{- if .Chart.AppVersion -}} +{{- $labels = mustMergeOverwrite $labels (dict "app.kubernetes.io/version" .Chart.AppVersion) -}} +{{- end -}} + +{{- $globalUserLabels := $global.labels | default dict -}} +{{- $localUserLabels := .Values.labels | default dict -}} + +{{- $labels = mustMergeOverwrite $labels $globalUserLabels $localUserLabels -}} + +{{- toYaml $labels -}} +{{- end -}} + + + +{{/* +This will render the labels that should be used in deployments/daemonsets template pods as a selector. +*/}} +{{- define "newrelic.common.labels.selectorLabels" -}} +{{- $name := dict "app.kubernetes.io/name" ( include "newrelic.common.naming.name" . ) -}} +{{- $instance := dict "app.kubernetes.io/instance" .Release.Name -}} + +{{- $selectorLabels := mustMergeOverwrite $name $instance -}} + +{{- toYaml $selectorLabels -}} +{{- end }} + + + +{{/* +Pod labels +*/}} +{{- define "newrelic.common.labels.podLabels" -}} +{{- $selectorLabels := fromYaml (include "newrelic.common.labels.selectorLabels" . ) -}} + +{{- $global := index .Values "global" | default dict -}} +{{- $globalPodLabels := $global.podLabels | default dict }} + +{{- $localPodLabels := .Values.podLabels | default dict }} + +{{- $podLabels := mustMergeOverwrite $selectorLabels $globalPodLabels $localPodLabels -}} + +{{- toYaml $podLabels -}} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_license.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_license.tpl new file mode 100644 index 0000000000..647b4ff439 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_license.tpl @@ -0,0 +1,56 @@ +{{/* +Return the name of the secret holding the License Key. +*/}} +{{- define "newrelic.common.license.secretName" -}} +{{- $default := include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "license" ) -}} +{{- include "newrelic.common.license._customSecretName" . | default $default -}} +{{- end -}} + +{{/* +Return the name key for the License Key inside the secret. +*/}} +{{- define "newrelic.common.license.secretKeyName" -}} +{{- include "newrelic.common.license._customSecretKey" . | default "licenseKey" -}} +{{- end -}} + +{{/* +Return local licenseKey if set, global otherwise. +This helper is for internal use. +*/}} +{{- define "newrelic.common.license._licenseKey" -}} +{{- if .Values.licenseKey -}} + {{- .Values.licenseKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.licenseKey -}} + {{- .Values.global.licenseKey -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name of the secret holding the License Key. +This helper is for internal use. +*/}} +{{- define "newrelic.common.license._customSecretName" -}} +{{- if .Values.customSecretName -}} + {{- .Values.customSecretName -}} +{{- else if .Values.global -}} + {{- if .Values.global.customSecretName -}} + {{- .Values.global.customSecretName -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name key for the License Key inside the secret. +This helper is for internal use. +*/}} +{{- define "newrelic.common.license._customSecretKey" -}} +{{- if .Values.customSecretLicenseKey -}} + {{- .Values.customSecretLicenseKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.customSecretLicenseKey }} + {{- .Values.global.customSecretLicenseKey -}} + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_license_secret.yaml.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_license_secret.yaml.tpl new file mode 100644 index 0000000000..610a0a3370 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_license_secret.yaml.tpl @@ -0,0 +1,21 @@ +{{/* +Renders the license key secret if user has not specified a custom secret. +*/}} +{{- define "newrelic.common.license.secret" }} +{{- if not (include "newrelic.common.license._customSecretName" .) }} +{{- /* Fail if licenseKey is empty and required: */ -}} +{{- if not (include "newrelic.common.license._licenseKey" .) }} + {{- fail "You must specify a licenseKey or a customSecretName containing it" }} +{{- end }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "newrelic.common.license.secretName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +data: + {{ include "newrelic.common.license.secretKeyName" . }}: {{ include "newrelic.common.license._licenseKey" . | b64enc }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_low-data-mode.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_low-data-mode.tpl new file mode 100644 index 0000000000..3dd55ef2ff --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_low-data-mode.tpl @@ -0,0 +1,26 @@ +{{- /* +Abstraction of the lowDataMode toggle. +This helper allows to override the global `.global.lowDataMode` with the value of `.lowDataMode`. +Returns "true" if `lowDataMode` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.lowDataMode" -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if (get .Values "lowDataMode" | kindIs "bool") -}} + {{- if .Values.lowDataMode -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.lowDataMode" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.lowDataMode -}} + {{- end -}} +{{- else -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "lowDataMode" | kindIs "bool" -}} + {{- if $global.lowDataMode -}} + {{- $global.lowDataMode -}} + {{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_naming.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_naming.tpl new file mode 100644 index 0000000000..19fa92648c --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_naming.tpl @@ -0,0 +1,73 @@ +{{/* +This is an function to be called directly with a string just to truncate strings to +63 chars because some Kubernetes name fields are limited to that. +*/}} +{{- define "newrelic.common.naming.truncateToDNS" -}} +{{- . | trunc 63 | trimSuffix "-" }} +{{- end }} + + + +{{- /* +Given a name and a suffix returns a 'DNS Valid' which always include the suffix, truncating the name if needed. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If suffix is too long it gets truncated but it always takes precedence over name, so a 63 chars suffix would suppress the name. +Usage: +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" "" "suffix" "my-suffix" ) }} +*/ -}} +{{- define "newrelic.common.naming.truncateToDNSWithSuffix" -}} +{{- $suffix := (include "newrelic.common.naming.truncateToDNS" .suffix) -}} +{{- $maxLen := (max (sub 63 (add1 (len $suffix))) 0) -}} {{- /* We prepend "-" to the suffix so an additional character is needed */ -}} + +{{- $newName := .name | trunc ($maxLen | int) | trimSuffix "-" -}} +{{- if $newName -}} +{{- printf "%s-%s" $newName $suffix -}} +{{- else -}} +{{ $suffix }} +{{- end -}} + +{{- end -}} + + + +{{/* +Expand the name of the chart. +Uses the Chart name by default if nameOverride is not set. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "newrelic.common.naming.name" -}} +{{- $name := .Values.nameOverride | default .Chart.Name -}} +{{- include "newrelic.common.naming.truncateToDNS" $name -}} +{{- end }} + + + +{{/* +Create a default fully qualified app name. +By default the full name will be "" just in if it has the chart name included in that, if not +it will be concatenated like "-". This could change if fullnameOverride or +nameOverride are set. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "newrelic.common.naming.fullname" -}} +{{- $name := include "newrelic.common.naming.name" . -}} + +{{- if .Values.fullnameOverride -}} + {{- $name = .Values.fullnameOverride -}} +{{- else if not (contains $name .Release.Name) -}} + {{- $name = printf "%s-%s" .Release.Name $name -}} +{{- end -}} + +{{- include "newrelic.common.naming.truncateToDNS" $name -}} + +{{- end -}} + + + +{{/* +Create chart name and version as used by the chart label. +This function should not be used for naming objects. Use "common.naming.{name,fullname}" instead. +*/}} +{{- define "newrelic.common.naming.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_nodeselector.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_nodeselector.tpl new file mode 100644 index 0000000000..d488873412 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_nodeselector.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod nodeSelector */ -}} +{{- define "newrelic.common.nodeSelector" -}} + {{- if .Values.nodeSelector -}} + {{- toYaml .Values.nodeSelector -}} + {{- else if .Values.global -}} + {{- if .Values.global.nodeSelector -}} + {{- toYaml .Values.global.nodeSelector -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_priority-class-name.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_priority-class-name.tpl new file mode 100644 index 0000000000..50182b7343 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_priority-class-name.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the pod priorityClassName */ -}} +{{- define "newrelic.common.priorityClassName" -}} + {{- if .Values.priorityClassName -}} + {{- .Values.priorityClassName -}} + {{- else if .Values.global -}} + {{- if .Values.global.priorityClassName -}} + {{- .Values.global.priorityClassName -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_privileged.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_privileged.tpl new file mode 100644 index 0000000000..f3ae814dd9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_privileged.tpl @@ -0,0 +1,28 @@ +{{- /* +This is a helper that returns whether the chart should assume the user is fine deploying privileged pods. +*/ -}} +{{- define "newrelic.common.privileged" -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists. */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if get .Values "privileged" | kindIs "bool" -}} + {{- if .Values.privileged -}} + {{- .Values.privileged -}} + {{- end -}} +{{- else if get $global "privileged" | kindIs "bool" -}} + {{- if $global.privileged -}} + {{- $global.privileged -}} + {{- end -}} +{{- end -}} +{{- end -}} + + + +{{- /* Return directly "true" or "false" based in the exist of "newrelic.common.privileged" */ -}} +{{- define "newrelic.common.privileged.value" -}} +{{- if include "newrelic.common.privileged" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_proxy.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_proxy.tpl new file mode 100644 index 0000000000..60f34c7ec1 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_proxy.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the proxy */ -}} +{{- define "newrelic.common.proxy" -}} + {{- if .Values.proxy -}} + {{- .Values.proxy -}} + {{- else if .Values.global -}} + {{- if .Values.global.proxy -}} + {{- .Values.global.proxy -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_security-context.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_security-context.tpl new file mode 100644 index 0000000000..9edfcabfd0 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_security-context.tpl @@ -0,0 +1,23 @@ +{{- /* Defines the container securityContext context */ -}} +{{- define "newrelic.common.securityContext.container" -}} +{{- $global := index .Values "global" | default dict -}} + +{{- if .Values.containerSecurityContext -}} + {{- toYaml .Values.containerSecurityContext -}} +{{- else if $global.containerSecurityContext -}} + {{- toYaml $global.containerSecurityContext -}} +{{- end -}} +{{- end -}} + + + +{{- /* Defines the pod securityContext context */ -}} +{{- define "newrelic.common.securityContext.pod" -}} +{{- $global := index .Values "global" | default dict -}} + +{{- if .Values.podSecurityContext -}} + {{- toYaml .Values.podSecurityContext -}} +{{- else if $global.podSecurityContext -}} + {{- toYaml $global.podSecurityContext -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_serviceaccount.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_serviceaccount.tpl new file mode 100644 index 0000000000..2d352f6ea9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_serviceaccount.tpl @@ -0,0 +1,90 @@ +{{- /* Defines if the service account has to be created or not */ -}} +{{- define "newrelic.common.serviceAccount.create" -}} +{{- $valueFound := false -}} + +{{- /* Look for a global creation of a service account */ -}} +{{- if get .Values "serviceAccount" | kindIs "map" -}} + {{- if (get .Values.serviceAccount "create" | kindIs "bool") -}} + {{- $valueFound = true -}} + {{- if .Values.serviceAccount.create -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.serviceAccount.name" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.serviceAccount.create -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- /* Look for a local creation of a service account */ -}} +{{- if not $valueFound -}} + {{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} + {{- $global := index .Values "global" | default dict -}} + {{- if get $global "serviceAccount" | kindIs "map" -}} + {{- if get $global.serviceAccount "create" | kindIs "bool" -}} + {{- $valueFound = true -}} + {{- if $global.serviceAccount.create -}} + {{- $global.serviceAccount.create -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- /* In case no serviceAccount value has been found, default to "true" */ -}} +{{- if not $valueFound -}} +true +{{- end -}} +{{- end -}} + + + +{{- /* Defines the name of the service account */ -}} +{{- define "newrelic.common.serviceAccount.name" -}} +{{- $localServiceAccount := "" -}} +{{- if get .Values "serviceAccount" | kindIs "map" -}} + {{- if (get .Values.serviceAccount "name" | kindIs "string") -}} + {{- $localServiceAccount = .Values.serviceAccount.name -}} + {{- end -}} +{{- end -}} + +{{- $globalServiceAccount := "" -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "serviceAccount" | kindIs "map" -}} + {{- if get $global.serviceAccount "name" | kindIs "string" -}} + {{- $globalServiceAccount = $global.serviceAccount.name -}} + {{- end -}} +{{- end -}} + +{{- if (include "newrelic.common.serviceAccount.create" .) -}} + {{- $localServiceAccount | default $globalServiceAccount | default (include "newrelic.common.naming.fullname" .) -}} +{{- else -}} + {{- $localServiceAccount | default $globalServiceAccount | default "default" -}} +{{- end -}} +{{- end -}} + + + +{{- /* Merge the global and local annotations for the service account */ -}} +{{- define "newrelic.common.serviceAccount.annotations" -}} +{{- $localServiceAccount := dict -}} +{{- if get .Values "serviceAccount" | kindIs "map" -}} + {{- if get .Values.serviceAccount "annotations" -}} + {{- $localServiceAccount = .Values.serviceAccount.annotations -}} + {{- end -}} +{{- end -}} + +{{- $globalServiceAccount := dict -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "serviceAccount" | kindIs "map" -}} + {{- if get $global.serviceAccount "annotations" -}} + {{- $globalServiceAccount = $global.serviceAccount.annotations -}} + {{- end -}} +{{- end -}} + +{{- $merged := mustMergeOverwrite $globalServiceAccount $localServiceAccount -}} + +{{- if $merged -}} + {{- toYaml $merged -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_staging.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_staging.tpl new file mode 100644 index 0000000000..bd9ad09bb9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_staging.tpl @@ -0,0 +1,39 @@ +{{- /* +Abstraction of the nrStaging toggle. +This helper allows to override the global `.global.nrStaging` with the value of `.nrStaging`. +Returns "true" if `nrStaging` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.nrStaging" -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if (get .Values "nrStaging" | kindIs "bool") -}} + {{- if .Values.nrStaging -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.nrStaging" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.nrStaging -}} + {{- end -}} +{{- else -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "nrStaging" | kindIs "bool" -}} + {{- if $global.nrStaging -}} + {{- $global.nrStaging -}} + {{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} + + + +{{- /* +Returns "true" of "false" directly instead of empty string (Helm falsiness) based on the exit of "newrelic.common.nrStaging" +*/ -}} +{{- define "newrelic.common.nrStaging.value" -}} +{{- if include "newrelic.common.nrStaging" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_tolerations.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_tolerations.tpl new file mode 100644 index 0000000000..e016b38e27 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_tolerations.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod tolerations */ -}} +{{- define "newrelic.common.tolerations" -}} + {{- if .Values.tolerations -}} + {{- toYaml .Values.tolerations -}} + {{- else if .Values.global -}} + {{- if .Values.global.tolerations -}} + {{- toYaml .Values.global.tolerations -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_verbose-log.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_verbose-log.tpl new file mode 100644 index 0000000000..2286d46815 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/templates/_verbose-log.tpl @@ -0,0 +1,54 @@ +{{- /* +Abstraction of the verbose toggle. +This helper allows to override the global `.global.verboseLog` with the value of `.verboseLog`. +Returns "true" if `verbose` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.verboseLog" -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if (get .Values "verboseLog" | kindIs "bool") -}} + {{- if .Values.verboseLog -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.verboseLog" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.verboseLog -}} + {{- end -}} +{{- else -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "verboseLog" | kindIs "bool" -}} + {{- if $global.verboseLog -}} + {{- $global.verboseLog -}} + {{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} + + + +{{- /* +Abstraction of the verbose toggle. +This helper abstracts the function "newrelic.common.verboseLog" to return true or false directly. +*/ -}} +{{- define "newrelic.common.verboseLog.valueAsBoolean" -}} +{{- if include "newrelic.common.verboseLog" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} + + + +{{- /* +Abstraction of the verbose toggle. +This helper abstracts the function "newrelic.common.verboseLog" to return 1 or 0 directly. +*/ -}} +{{- define "newrelic.common.verboseLog.valueAsInt" -}} +{{- if include "newrelic.common.verboseLog" . -}} +1 +{{- else -}} +0 +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/values.yaml new file mode 100644 index 0000000000..75e2d112ad --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/charts/common-library/values.yaml @@ -0,0 +1 @@ +# values are not needed for the library chart, however this file is still needed for helm lint to work. diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/ci/test-cplane-kind-deployment-values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/ci/test-cplane-kind-deployment-values.yaml new file mode 100644 index 0000000000..1e2c36d21f --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/ci/test-cplane-kind-deployment-values.yaml @@ -0,0 +1,135 @@ +global: + licenseKey: 1234567890abcdef1234567890abcdef12345678 + cluster: test-cluster + +common: + agentConfig: + # We set it in order for the kubelet to not crash when posting tho the agent. Since the License_Key is + # not valid, the Identity Api doesn't return an AgentID and the server from the Agent takes to long to respond + is_forward_only: true + config: + sink: + http: + timeout: 180s + +customAttributes: + new: relic + loren: ipsum + +# Disable KSM scraper as it is not enabled when testing this chart individually. +ksm: + enabled: false + +# K8s DaemonSets update strategy. +updateStrategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 + +enableProcessMetrics: "false" +serviceAccount: + create: true + +podAnnotations: + annotation1: "annotation" +podLabels: + label1: "label" + +securityContext: + runAsUser: 1000 + runAsGroup: 2000 + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + +privileged: true + +rbac: + create: true + pspEnabled: false + +prefixDisplayNameWithCluster: false +useNodeNameAsDisplayName: true +integrations_config: [] + +kubelet: + enabled: true + annotations: {} + tolerations: + - operator: "Exists" + effect: "NoSchedule" + - operator: "Exists" + effect: "NoExecute" + extraEnv: + - name: ENV_VAR1 + value: "var1" + - name: ENV_VAR2 + value: "var2" + resources: + limits: + memory: 400M + requests: + cpu: 100m + memory: 180M + config: + scheme: "http" + +controlPlane: + kind: Deployment + enabled: true + config: + etcd: + enabled: true + autodiscover: + - selector: "tier=control-plane,component=etcd" + namespace: kube-system + matchNode: true + endpoints: + - url: https://localhost:4001 + insecureSkipVerify: true + auth: + type: bearer + - url: http://localhost:2381 + scheduler: + enabled: true + autodiscover: + - selector: "tier=control-plane,component=kube-scheduler" + namespace: kube-system + matchNode: true + endpoints: + - url: https://localhost:10259 + insecureSkipVerify: true + auth: + type: bearer + controllerManager: + enabled: true + autodiscover: + - selector: "tier=control-plane,component=kube-controller-manager" + namespace: kube-system + matchNode: true + endpoints: + - url: https://localhost:10257 + insecureSkipVerify: true + auth: + type: bearer + mtls: + secretName: secret-name + secretNamespace: default + apiServer: + enabled: true + autodiscover: + - selector: "tier=control-plane,component=kube-apiserver" + namespace: kube-system + matchNode: true + endpoints: + - url: https://localhost:8443 + insecureSkipVerify: true + auth: + type: bearer + mtls: + secretName: secret-name4 + - url: http://localhost:8080 + +images: + integration: + tag: test + repository: e2e/nri-kubernetes diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/ci/test-values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/ci/test-values.yaml new file mode 100644 index 0000000000..125a496079 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/ci/test-values.yaml @@ -0,0 +1,134 @@ +global: + licenseKey: 1234567890abcdef1234567890abcdef12345678 + cluster: test-cluster + +common: + agentConfig: + # We set it in order for the kubelet to not crash when posting tho the agent. Since the License_Key is + # not valid, the Identity Api doesn't return an AgentID and the server from the Agent takes to long to respond + is_forward_only: true + config: + sink: + http: + timeout: 180s + +customAttributes: + new: relic + loren: ipsum + +# Disable KSM scraper as it is not enabled when testing this chart individually. +ksm: + enabled: false + +# K8s DaemonSets update strategy. +updateStrategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 + +enableProcessMetrics: "false" +serviceAccount: + create: true + +podAnnotations: + annotation1: "annotation" +podLabels: + label1: "label" + +securityContext: + runAsUser: 1000 + runAsGroup: 2000 + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + +privileged: true + +rbac: + create: true + pspEnabled: false + +prefixDisplayNameWithCluster: false +useNodeNameAsDisplayName: true +integrations_config: [] + +kubelet: + enabled: true + annotations: {} + tolerations: + - operator: "Exists" + effect: "NoSchedule" + - operator: "Exists" + effect: "NoExecute" + extraEnv: + - name: ENV_VAR1 + value: "var1" + - name: ENV_VAR2 + value: "var2" + resources: + limits: + memory: 400M + requests: + cpu: 100m + memory: 180M + config: + scheme: "http" + +controlPlane: + enabled: true + config: + etcd: + enabled: true + autodiscover: + - selector: "tier=control-plane,component=etcd" + namespace: kube-system + matchNode: true + endpoints: + - url: https://localhost:4001 + insecureSkipVerify: true + auth: + type: bearer + - url: http://localhost:2381 + scheduler: + enabled: true + autodiscover: + - selector: "tier=control-plane,component=kube-scheduler" + namespace: kube-system + matchNode: true + endpoints: + - url: https://localhost:10259 + insecureSkipVerify: true + auth: + type: bearer + controllerManager: + enabled: true + autodiscover: + - selector: "tier=control-plane,component=kube-controller-manager" + namespace: kube-system + matchNode: true + endpoints: + - url: https://localhost:10257 + insecureSkipVerify: true + auth: + type: bearer + mtls: + secretName: secret-name + secretNamespace: default + apiServer: + enabled: true + autodiscover: + - selector: "tier=control-plane,component=kube-apiserver" + namespace: kube-system + matchNode: true + endpoints: + - url: https://localhost:8443 + insecureSkipVerify: true + auth: + type: bearer + mtls: + secretName: secret-name4 + - url: http://localhost:8080 + +images: + integration: + tag: test + repository: e2e/nri-kubernetes diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/NOTES.txt b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/NOTES.txt new file mode 100644 index 0000000000..16cc6ea13d --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/NOTES.txt @@ -0,0 +1,131 @@ +{{- if not .Values.forceUnsupportedInterval }} +{{- $max := 40 }} +{{- $min := 10 }} +{{- if not (.Values.common.config.interval | hasSuffix "s") }} +{{ fail (printf "Interval must be between %ds and %ds" $min $max ) }} +{{- end }} +{{- if gt ( .Values.common.config.interval | trimSuffix "s" | int64 ) $max }} +{{ fail (printf "Intervals larger than %ds are not supported" $max) }} +{{- end }} +{{- if lt ( .Values.common.config.interval | trimSuffix "s" | int64 ) $min }} +{{ fail (printf "Intervals smaller than %ds are not supported" $min) }} +{{- end }} +{{- end }} + +{{- if or (not .Values.ksm.enabled) (not .Values.kubelet.enabled) }} +Warning: +======== + +You have specified ksm or kubelet integration components as not enabled. +Those components are needed to have the full experience on NROne kubernetes explorer. +{{- end }} + +{{- if and .Values.controlPlane.enabled (not (include "nriKubernetes.controlPlane.hostNetwork" .)) }} +Warning: +======== + +Most Control Plane components listen in the loopback address only, which is not reachable without `hostNetwork: true`. +Control plane autodiscovery might not work as expected. +You can enable hostNetwork for all pods by setting `global.hotNetwork`, `hostNetwork` or only for the control +plane pods by setting `controlPlane.hostNetwork: true`. Alternatively, you can disable control plane monitoring altogether with +`controlPlane.enabled: false`. +{{- end }} + +{{- if and (include "newrelic.fargate" .) .Values.kubelet.affinity }} +Warning: +======== + +You have specified both an EKS Fargate environment (global.fargate) and custom +nodeAffinity rules, so we couldn't automatically exclude the kubelet daemonSet from +Fargate nodes. In order for the integration to work, you MUST manually exclude +the daemonSet from Fargate nodes. + +Please make sure your `values.yaml' contains a .kubelet.affinity.nodeAffinity that achieve the same effect as: + +affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: eks.amazonaws.com/compute-type + operator: NotIn + values: + - fargate +{{- end }} + +{{- if and .Values.nodeAffinity .Values.controlPlane.enabled }} +WARNING: `nodeAffinity` is deprecated +===================================== + +We have applied the old `nodeAffinity` to KSM and Kubelet components, but *NOT* to the control plane component as it +might conflict with the default nodeSelector. +This shimming will be removed in the future, please convert your `nodeAffinity` item into: +`ksm.affinity.nodeAffinity`, `controlPlane.affinity.nodeAffinity`, and `kubelet.affinity.nodeAffinity`. +{{- end }} + +{{- if and .Values.integrations_config }} +WARNING: `integrations_config` is deprecated +============================================ + +We have automatically translated `integrations_config` to the new format, but this shimming will be removed in the +future. Please migrate your configs to the new format in the `integrations` key. +{{- end }} + +{{- if or .Values.kubeStateMetricsScheme .Values.kubeStateMetricsPort .Values.kubeStateMetricsUrl .Values.kubeStateMetricsPodLabel .Values.kubeStateMetricsNamespace }} +WARNING: `kubeStateMetrics*` are deprecated +=========================================== + +We have automatically translated your `kubeStateMetrics*` values to the new format, but this shimming will be removed in +the future. Please migrate your configs to the new format in the `ksm.config` key. +{{- end }} + +{{- if .Values.runAsUser }} +WARNING: `runAsUser` is deprecated +================================== + +We have automatically translated your `runAsUser` setting to the new format, but this shimming will be removed in the +future. Please migrate your configs to the new format in the `securityContext` key. +{{- end }} + +{{- if .Values.config }} +WARNING: `config` is deprecated +=============================== + +We have automatically translated your `config` setting to the new format, but this shimming will be removed in the +future. Please migrate your agent config to the new format in the `common.agentConfig` key. +{{- end }} + + +{{ $errors:= "" }} + +{{- if .Values.logFile }} +{{ $errors = printf "%s\n\n%s" $errors (include "newrelic.compatibility.message.logFile" . ) }} +{{- end }} + +{{- if .Values.resources }} +{{ $errors = printf "%s\n\n%s" $errors (include "newrelic.compatibility.message.resources" . ) }} +{{- end }} + +{{- if .Values.image }} +{{ $errors = printf "%s\n\n%s" $errors (include "newrelic.compatibility.message.image" . ) }} +{{- end }} + +{{- if .Values.enableWindows }} +{{ $errors = printf "%s\n\n%s" $errors (include "newrelic.compatibility.message.windows" . ) }} +{{- end }} + +{{- if ( or .Values.controllerManagerEndpointUrl .Values.schedulerEndpointUrl .Values.etcdEndpointUrl .Values.apiServerEndpointUrl )}} +{{ $errors = printf "%s\n\n%s" $errors (include "newrelic.compatibility.message.apiURL" . ) }} +{{- end }} + +{{- if ( or .Values.etcdTlsSecretName .Values.etcdTlsSecretNamespace )}} +{{ $errors = printf "%s\n\n%s" $errors (include "newrelic.compatibility.message.etcdSecrets" . ) }} +{{- end }} + +{{- if .Values.apiServerSecurePort }} +{{ $errors = printf "%s\n\n%s" $errors (include "newrelic.compatibility.message.apiServerSecurePort" . ) }} +{{- end }} + +{{- if $errors | trim}} +{{- fail (printf "\n\n%s\n%s" (include "newrelic.compatibility.message.common" . ) $errors ) }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/_helpers.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/_helpers.tpl new file mode 100644 index 0000000000..033ef0bfcf --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/_helpers.tpl @@ -0,0 +1,118 @@ +{{/* +Create a default fully qualified app name. + +This is a copy and paste from the common-library's name helper because the overriding system was broken. +As we have to change the logic to use "nrk8s" instead of `.Chart.Name` we need to maintain here a version +of the fullname helper + +By default the full name will be "" just in if it has "nrk8s" included in that, if not +it will be concatenated like "-nrk8s". This could change if fullnameOverride or +nameOverride are set. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "nriKubernetes.naming.fullname" -}} +{{- $name := .Values.nameOverride | default "nrk8s" -}} + +{{- if .Values.fullnameOverride -}} + {{- $name = .Values.fullnameOverride -}} +{{- else if not (contains $name .Release.Name) -}} + {{- $name = printf "%s-%s" .Release.Name $name -}} +{{- end -}} + +{{- include "newrelic.common.naming.truncateToDNS" $name -}} +{{- end -}} + + + +{{- /* Naming helpers*/ -}} +{{- define "nriKubernetes.naming.secrets" }} +{{- include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "nriKubernetes.naming.fullname" .) "suffix" "secrets") -}} +{{- end -}} + + + +{{- /* Return a YAML with the mode to be added to the labels */ -}} +{{- define "nriKubernetes._mode" -}} +{{- if include "newrelic.common.privileged" . -}} + mode: privileged +{{- else -}} + mode: unprivileged +{{- end -}} +{{- end -}} + + + +{{/* +Add `mode` label to the labels that come from the common library for all the objects +*/}} +{{- define "nriKubernetes.labels" -}} +{{- $labels := include "newrelic.common.labels" . | fromYaml -}} +{{- $mode := fromYaml ( include "nriKubernetes._mode" . ) -}} + +{{- mustMergeOverwrite $labels $mode | toYaml -}} +{{- end -}} + + + +{{/* +Add `mode` label to the labels that come from the common library for podLabels +*/}} +{{- define "nriKubernetes.labels.podLabels" -}} +{{- $labels := include "newrelic.common.labels.podLabels" . | fromYaml -}} +{{- $mode := fromYaml ( include "nriKubernetes._mode" . ) -}} + +{{- mustMergeOverwrite $labels $mode | toYaml -}} +{{- end -}} + + + +{{/* +Returns fargate +*/}} +{{- define "newrelic.fargate" -}} +{{- if .Values.fargate -}} + {{- .Values.fargate -}} +{{- else if .Values.global -}} + {{- if .Values.global.fargate -}} + {{- .Values.global.fargate -}} + {{- end -}} +{{- end -}} +{{- end -}} + + + +{{- define "newrelic.integrationConfigDefaults" -}} +{{- if include "newrelic.common.lowDataMode" . -}} +interval: 30s +{{- else -}} +interval: 15s +{{- end -}} +{{- end -}} + + + +{{- /* These are the defaults that are used for all the containers in this chart (except the kubelet's agent */ -}} +{{- define "nriKubernetes.securityContext.containerDefaults" -}} +runAsUser: 1000 +runAsGroup: 2000 +allowPrivilegeEscalation: false +readOnlyRootFilesystem: true +{{- end -}} + + + +{{- /* Allow to change pod defaults dynamically based if we are running in privileged mode or not */ -}} +{{- define "nriKubernetes.securityContext.container" -}} +{{- $defaults := fromYaml ( include "nriKubernetes.securityContext.containerDefaults" . ) -}} +{{- $compatibilityLayer := include "newrelic.compatibility.securityContext" . | fromYaml -}} +{{- $commonLibrary := include "newrelic.common.securityContext.container" . | fromYaml -}} + +{{- $finalSecurityContext := dict -}} +{{- if $commonLibrary -}} + {{- $finalSecurityContext = mustMergeOverwrite $commonLibrary $compatibilityLayer -}} +{{- else -}} + {{- $finalSecurityContext = mustMergeOverwrite $defaults $compatibilityLayer -}} +{{- end -}} + +{{- toYaml $finalSecurityContext -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/_helpers_compatibility.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/_helpers_compatibility.tpl new file mode 100644 index 0000000000..07365e5a1a --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/_helpers_compatibility.tpl @@ -0,0 +1,202 @@ +{{/* +Returns true if .Values.ksm.enabled is true and the legacy disableKubeStateMetrics is not set +*/}} +{{- define "newrelic.compatibility.ksm.enabled" -}} +{{- if and .Values.ksm.enabled (not .Values.disableKubeStateMetrics) -}} +true +{{- end -}} +{{- end -}} + +{{/* +Returns legacy ksm values +*/}} +{{- define "newrelic.compatibility.ksm.legacyData" -}} +enabled: true +{{- if .Values.kubeStateMetricsScheme }} +scheme: {{ .Values.kubeStateMetricsScheme }} +{{- end -}} +{{- if .Values.kubeStateMetricsPort }} +port: {{ .Values.kubeStateMetricsPort }} +{{- end -}} +{{- if .Values.kubeStateMetricsUrl }} +staticURL: {{ .Values.kubeStateMetricsUrl }} +{{- end -}} +{{- if .Values.kubeStateMetricsPodLabel }} +selector: {{ printf "%s=kube-state-metrics" .Values.kubeStateMetricsPodLabel }} +{{- end -}} +{{- if .Values.kubeStateMetricsNamespace }} +namespace: {{ .Values.kubeStateMetricsNamespace}} +{{- end -}} +{{- end -}} + +{{/* +Returns the new value if available, otherwise falling back on the legacy one +*/}} +{{- define "newrelic.compatibility.valueWithFallback" -}} +{{- if .supported }} +{{- toYaml .supported}} +{{- else if .legacy -}} +{{- toYaml .legacy}} +{{- end }} +{{- end -}} + +{{/* +Returns a dictionary with legacy runAsUser config +*/}} +{{- define "newrelic.compatibility.securityContext" -}} +{{- if .Values.runAsUser -}} +{{ dict "runAsUser" .Values.runAsUser | toYaml }} +{{- end -}} +{{- end -}} + +{{/* +Returns legacy annotations if available +*/}} +{{- define "newrelic.compatibility.annotations" -}} +{{- with .Values.daemonSet -}} +{{- with .annotations -}} +{{- toYaml . }} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Returns agent configmap merged with legacy config and legacy eventQueueDepth config +*/}} +{{- define "newrelic.compatibility.agentConfig" -}} +{{- $oldConfig := deepCopy (.Values.config | default dict) -}} +{{- $newConfig := deepCopy .Values.common.agentConfig -}} +{{- $eventQueueDepth := dict -}} + +{{- if .Values.eventQueueDepth -}} +{{- $eventQueueDepth = dict "event_queue_depth" .Values.eventQueueDepth -}} +{{- end -}} + +{{- mustMergeOverwrite $oldConfig $newConfig $eventQueueDepth | toYaml -}} +{{- end -}} + +{{- /* +Return a valid podSpec.affinity object from the old `.Values.nodeAffinity`. +*/ -}} +{{- define "newrelic.compatibility.nodeAffinity" -}} +{{- if .Values.nodeAffinity -}} +nodeAffinity: + {{- toYaml .Values.nodeAffinity | nindent 2 }} +{{- end -}} +{{- end -}} + +{{/* +Returns legacy integrations_config configmap data +*/}} +{{- define "newrelic.compatibility.integrations" -}} +{{- if .Values.integrations_config -}} +{{- range .Values.integrations_config }} +{{ .name -}}: |- + {{- toYaml .data | nindent 2 }} +{{- end -}} +{{- end -}} +{{- end -}} + +{{- define "newrelic.compatibility.message.logFile" -}} +The 'logFile' option is no longer supported and has been replaced by: + - common.agentConfig.log_file. + +------ +{{- end -}} + +{{- define "newrelic.compatibility.message.resources" -}} +You have specified the legacy 'resources' option in your values, which is not fully compatible with the v3 version. +This version deploys three different components and therefore you'll need to specify resources for each of them. +Please use + - ksm.resources, + - controlPlane.resources, + - kubelet.resources. + +------ +{{- end -}} + +{{- define "newrelic.compatibility.message.apiServerSecurePort" -}} +You have specified the legacy 'apiServerSecurePort' option in your values, which is not fully compatible with the v3 +version. +Please configure the API Server port as a part of 'apiServer.autodiscover[].endpoints' + +------ +{{- end -}} + +{{- define "newrelic.compatibility.message.windows" -}} +nri-kubernetes v3 does not support deploying into windows Nodes. +Please use the latest 2.x version of the chart. + +------ +{{- end -}} + +{{- define "newrelic.compatibility.message.etcdSecrets" -}} +Values "etcdTlsSecretName" and "etcdTlsSecretNamespace" are no longer supported, please specify them as a part of the +'etcd' config in the values, for example: + - endpoints: + - url: https://localhost:9979 + insecureSkipVerify: true + auth: + type: mTLS + mtls: + secretName: {{ .Values.etcdTlsSecretName | default "etcdTlsSecretName"}} + secretNamespace: {{ .Values.etcdTlsSecretNamespace | default "etcdTlsSecretNamespace"}} + +------ +{{- end -}} + +{{- define "newrelic.compatibility.message.apiURL" -}} +Values "controllerManagerEndpointUrl", "etcdEndpointUrl", "apiServerEndpointUrl", "schedulerEndpointUrl" are no longer +supported, please specify them as a part of the 'controlplane' config in the values, for example + autodiscover: + - selector: "tier=control-plane,component=etcd" + namespace: kube-system + matchNode: true + endpoints: + - url: https://localhost:4001 + insecureSkipVerify: true + auth: + type: bearer + +------ +{{- end -}} + +{{- define "newrelic.compatibility.message.image" -}} +Configuring image repository an tag under 'image' is no longer supported. +The following values are no longer supported and are currently ignored: + - image.repository + - image.tag + - image.pullPolicy + - image.pullSecrets + +Notice that the 3.x version of the integration uses 3 different images. +Please set: + - images.forwarder.* to configure the infrastructure-agent forwarder. + - images.agent.* to configure the image bundling the infrastructure-agent and on-host integrations. + - images.integration.* to configure the image in charge of scraping k8s data. + +------ +{{- end -}} + +{{- define "newrelic.compatibility.message.customAttributes" -}} +We still support using custom attributes but we support it as a map and dropped it as a string. +customAttributes: {{ .Values.customAttributes | quote }} + +You should change your values to something like this: + +customAttributes: +{{- range $k, $v := fromJson .Values.customAttributes -}} + {{- $k | nindent 2 }}: {{ $v | quote }} +{{- end }} + +**NOTE**: If you read above errors like "invalid character ':' after top-level value" or "json: cannot unmarshal string into Go value of type map[string]interface {}" means that the string you have in your values is not a valid JSON, Helm is not able to parse it and we could not show you how you should change it. Sorry. +{{- end -}} + +{{- define "newrelic.compatibility.message.common" -}} +###### +The chart cannot be rendered since the values listed below are not supported. Please replace those with the new ones compatible with newrelic-infrastructure V3. + +Keep in mind that the flag "--reuse-values" is not supported when migrating from V2 to V3. +Further information can be found in the official docs https://docs.newrelic.com/docs/kubernetes-pixie/kubernetes-integration/get-started/changes-since-v3#migration-guide" +###### +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/clusterrole.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/clusterrole.yaml new file mode 100644 index 0000000000..391dc1e1fd --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/clusterrole.yaml @@ -0,0 +1,35 @@ +{{- if .Values.rbac.create }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "newrelic.common.naming.fullname" . }} +rules: + - apiGroups: [""] + resources: + - "nodes/metrics" + - "nodes/stats" + - "nodes/proxy" + verbs: ["get", "list"] + - apiGroups: [ "" ] + resources: + - "endpoints" + - "services" + - "nodes" + - "namespaces" + - "pods" + verbs: [ "get", "list", "watch" ] + - nonResourceURLs: ["/metrics"] + verbs: ["get"] + {{- if .Values.rbac.pspEnabled }} + - apiGroups: + - extensions + resources: + - podsecuritypolicies + resourceNames: + - privileged-{{ include "newrelic.common.naming.fullname" . }} + verbs: + - use + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/clusterrolebinding.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/clusterrolebinding.yaml new file mode 100644 index 0000000000..fc5dfb8dae --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/clusterrolebinding.yaml @@ -0,0 +1,16 @@ +{{- if .Values.rbac.create }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "newrelic.common.naming.fullname" . }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "newrelic.common.naming.fullname" . }} +subjects: +- kind: ServiceAccount + name: {{ include "newrelic.common.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/_affinity_helper.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/_affinity_helper.tpl new file mode 100644 index 0000000000..320d16dae1 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/_affinity_helper.tpl @@ -0,0 +1,11 @@ +{{- /* +As this chart deploys what it should be three charts to maintain the transition to v3 as smooth as possible. +This means that this chart has 3 affinity so a helper should be done per scraper. +*/ -}} +{{- define "nriKubernetes.controlPlane.affinity" -}} +{{- if .Values.controlPlane.affinity -}} + {{- toYaml .Values.controlPlane.affinity -}} +{{- else if include "newrelic.common.affinity" . -}} + {{- include "newrelic.common.affinity" . -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/_agent-config_helper.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/_agent-config_helper.tpl new file mode 100644 index 0000000000..e113def82f --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/_agent-config_helper.tpl @@ -0,0 +1,20 @@ +{{- /* +Defaults for controlPlane's agent config +*/ -}} +{{- define "nriKubernetes.controlPlane.agentConfig.defaults" -}} +is_forward_only: true +http_server_enabled: true +http_server_port: 8001 +{{- end -}} + + + +{{- define "nriKubernetes.controlPlane.agentConfig" -}} +{{- $agentDefaults := fromYaml ( include "newrelic.common.agentConfig.defaults" . ) -}} +{{- $controlPlane := fromYaml ( include "nriKubernetes.controlPlane.agentConfig.defaults" . ) -}} +{{- $agentConfig := fromYaml ( include "newrelic.compatibility.agentConfig" . ) -}} +{{- $cpAgentConfig := .Values.controlPlane.agentConfig -}} +{{- $customAttributes := dict "custom_attributes" (dict "clusterName" (include "newrelic.common.cluster" . )) -}} + +{{- mustMergeOverwrite $agentDefaults $controlPlane $agentConfig $cpAgentConfig $customAttributes | toYaml -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/_host_network.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/_host_network.tpl new file mode 100644 index 0000000000..2f3bdf2d94 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/_host_network.tpl @@ -0,0 +1,22 @@ +{{/* Returns whether the controlPlane scraper should run with hostNetwork: true based on the user configuration. */}} +{{- define "nriKubernetes.controlPlane.hostNetwork" -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if get .Values.controlPlane "hostNetwork" | kindIs "bool" -}} + {{- if .Values.controlPlane.hostNetwork -}} + {{- .Values.controlPlane.hostNetwork -}} + {{- end -}} +{{- else if include "newrelic.common.hostNetwork" . -}} + {{- include "newrelic.common.hostNetwork" . -}} +{{- end -}} +{{- end -}} + + + +{{/* Abstraction of "nriKubernetes.controlPlane.hostNetwork" that returns true of false directly */}} +{{- define "nriKubernetes.controlPlane.hostNetwork.value" -}} +{{- if include "nriKubernetes.controlPlane.hostNetwork" . -}} + true +{{- else -}} + false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/_naming.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/_naming.tpl new file mode 100644 index 0000000000..4b9ef22e33 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/_naming.tpl @@ -0,0 +1,16 @@ +{{- /* Naming helpers*/ -}} +{{- define "nriKubernetes.controlplane.fullname" -}} +{{- include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "nriKubernetes.naming.fullname" .) "suffix" "controlplane") -}} +{{- end -}} + +{{- define "nriKubernetes.controlplane.fullname.agent" -}} +{{- include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "nriKubernetes.naming.fullname" .) "suffix" "agent-controlplane") -}} +{{- end -}} + +{{- define "nriKubernetes.controlplane.fullname.serviceAccount" -}} +{{- if include "newrelic.common.serviceAccount.create" . -}} + {{- include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "nriKubernetes.naming.fullname" .) "suffix" "controlplane") -}} +{{- else -}} + {{- include "newrelic.common.serviceAccount.name" . -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/_rbac.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/_rbac.tpl new file mode 100644 index 0000000000..a279df6b4c --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/_rbac.tpl @@ -0,0 +1,40 @@ +{{/* +Returns the list of namespaces where secrets need to be accessed by the controlPlane integration to do mTLS Auth +*/}} +{{- define "nriKubernetes.controlPlane.roleBindingNamespaces" -}} +{{ $namespaceList := list }} +{{- range $components := .Values.controlPlane.config }} + {{- if $components }} + {{- if kindIs "map" $components -}} + {{- if $components.staticEndpoint }} + {{- if $components.staticEndpoint.auth }} + {{- if $components.staticEndpoint.auth.mtls }} + {{- if $components.staticEndpoint.auth.mtls.secretNamespace }} + {{- $namespaceList = append $namespaceList $components.staticEndpoint.auth.mtls.secretNamespace -}} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- if $components.autodiscover }} + {{- range $autodiscover := $components.autodiscover }} + {{- if $autodiscover }} + {{- if $autodiscover.endpoints }} + {{- range $endpoint := $autodiscover.endpoints }} + {{- if $endpoint.auth }} + {{- if $endpoint.auth.mtls }} + {{- if $endpoint.auth.mtls.secretNamespace }} + {{- $namespaceList = append $namespaceList $endpoint.auth.mtls.secretNamespace -}} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} +roleBindingNamespaces: + {{- uniq $namespaceList | toYaml | nindent 2 }} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/_tolerations_helper.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/_tolerations_helper.tpl new file mode 100644 index 0000000000..3c82e82f52 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/_tolerations_helper.tpl @@ -0,0 +1,11 @@ +{{- /* +As this chart deploys what it should be three charts to maintain the transition to v3 as smooth as possible. +This means that this chart has 3 tolerations so a helper should be done per scraper. +*/ -}} +{{- define "nriKubernetes.controlPlane.tolerations" -}} +{{- if .Values.controlPlane.tolerations -}} + {{- toYaml .Values.controlPlane.tolerations -}} +{{- else if include "newrelic.common.tolerations" . -}} + {{- include "newrelic.common.tolerations" . -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/agent-configmap.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/agent-configmap.yaml new file mode 100644 index 0000000000..77f2e11dde --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/agent-configmap.yaml @@ -0,0 +1,18 @@ +{{- if .Values.controlPlane.enabled -}} +{{- if .Values.customAttributes | kindIs "string" }} +{{- fail ( include "newrelic.compatibility.message.customAttributes" . ) -}} +{{- else -}} +apiVersion: v1 +kind: ConfigMap +metadata: + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "nriKubernetes.controlplane.fullname.agent" . }} +data: + newrelic-infra.yml: |- + # This is the configuration file for the infrastructure agent. See: + # https://docs.newrelic.com/docs/infrastructure/install-infrastructure-agent/configuration/infrastructure-agent-configuration-settings/ + {{- include "nriKubernetes.controlPlane.agentConfig" . | nindent 4 }} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/clusterrole.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/clusterrole.yaml new file mode 100644 index 0000000000..57633e7f7b --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/clusterrole.yaml @@ -0,0 +1,47 @@ +{{- if and (.Values.controlPlane.enabled) (.Values.rbac.create) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "nriKubernetes.controlplane.fullname" . }} +rules: + - apiGroups: [""] + resources: + - "nodes/metrics" + - "nodes/stats" + - "nodes/proxy" + verbs: ["get", "list"] + - apiGroups: [ "" ] + resources: + - "pods" + - "nodes" + verbs: [ "get", "list", "watch" ] + - nonResourceURLs: ["/metrics"] + verbs: ["get", "head"] + {{- if .Values.rbac.pspEnabled }} + - apiGroups: + - extensions + resources: + - podsecuritypolicies + resourceNames: + - privileged-{{ include "newrelic.common.naming.fullname" . }} + verbs: + - use + {{- end -}} +{{- $namespaces := include "nriKubernetes.controlPlane.roleBindingNamespaces" . | fromYaml -}} +{{- if $namespaces.roleBindingNamespaces}} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "nriKubernetes.naming.secrets" . }} +rules: + - apiGroups: [""] + resources: + - "secrets" + verbs: ["get", "list", "watch"] +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/clusterrolebinding.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/clusterrolebinding.yaml new file mode 100644 index 0000000000..4e35300942 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/clusterrolebinding.yaml @@ -0,0 +1,16 @@ +{{- if and (.Values.controlPlane.enabled) (.Values.rbac.create) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "nriKubernetes.controlplane.fullname" . }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "nriKubernetes.controlplane.fullname" . }} +subjects: + - kind: ServiceAccount + name: {{ include "nriKubernetes.controlplane.fullname.serviceAccount" . }} + namespace: {{ .Release.Namespace }} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/daemonset.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/daemonset.yaml new file mode 100644 index 0000000000..938fc48d45 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/daemonset.yaml @@ -0,0 +1,205 @@ +{{- if and (.Values.controlPlane.enabled) (not (include "newrelic.fargate" .)) }} +apiVersion: apps/v1 +kind: {{ .Values.controlPlane.kind }} +metadata: + namespace: {{ .Release.Namespace }} + labels: + {{- include "nriKubernetes.labels" . | nindent 4 }} + name: {{ include "nriKubernetes.controlplane.fullname" . }} + {{- $legacyAnnotation:= fromYaml (include "newrelic.compatibility.annotations" .) -}} + {{- with include "newrelic.compatibility.valueWithFallback" (dict "legacy" $legacyAnnotation "supported" .Values.controlPlane.annotations )}} + annotations: {{ . | nindent 4 }} + {{- end }} +spec: + {{- if eq .Values.controlPlane.kind "DaemonSet"}} + {{- with .Values.updateStrategy }} + updateStrategy: {{ toYaml . | nindent 4 }} + {{- end }} + {{- end }} + {{- if eq .Values.controlPlane.kind "Deployment"}} + {{- with .Values.strategy }} + strategy: {{ toYaml . | nindent 4 }} + {{- end }} + {{- end }} + selector: + matchLabels: + {{- include "newrelic.common.labels.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: controlplane + template: + metadata: + annotations: + checksum/nri-kubernetes: {{ include (print $.Template.BasePath "/controlplane/scraper-configmap.yaml") . | sha256sum }} + checksum/agent-config: {{ include (print $.Template.BasePath "/controlplane/agent-configmap.yaml") . | sha256sum }} + {{- if include "newrelic.common.license.secret" . }}{{- /* If the is secret to template */}} + checksum/license-secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }} + {{- end }} + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "nriKubernetes.labels.podLabels" . | nindent 8 }} + app.kubernetes.io/component: controlplane + spec: + {{- with include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" (list .Values.images.pullSecrets) "context" .) }} + imagePullSecrets: + {{- . | nindent 8 }} + {{- end }} + {{- with include "newrelic.common.dnsConfig" . }} + dnsConfig: + {{- . | nindent 8 }} + {{- end }} + hostNetwork: {{ include "nriKubernetes.controlPlane.hostNetwork.value" . }} + {{- if include "nriKubernetes.controlPlane.hostNetwork" . }} + dnsPolicy: ClusterFirstWithHostNet + {{- end }} + {{- with include "newrelic.common.priorityClassName" . }} + priorityClassName: {{ . }} + {{- end }} + {{- with include "newrelic.common.securityContext.pod" . }} + securityContext: + {{- . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "nriKubernetes.controlplane.fullname.serviceAccount" . }} + + {{- if .Values.controlPlane.initContainers }} + initContainers: {{- tpl (.Values.controlPlane.initContainers | toYaml) . | nindent 8 }} + {{- end }} + containers: + - name: controlplane + image: {{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.images.integration "context" .) }} + imagePullPolicy: {{ .Values.images.integration.pullPolicy }} + {{- with include "nriKubernetes.securityContext.container" . | fromYaml }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + env: + - name: "NRI_KUBERNETES_SINK_HTTP_PORT" + value: {{ get (fromYaml (include "nriKubernetes.controlPlane.agentConfig" .)) "http_server_port" | quote }} + - name: "NRI_KUBERNETES_CLUSTERNAME" + value: {{ include "newrelic.common.cluster" . }} + - name: "NRI_KUBERNETES_VERBOSE" + value: {{ include "newrelic.common.verboseLog.valueAsBoolean" . | quote }} + + - name: "NRI_KUBERNETES_NODENAME" + valueFrom: + fieldRef: + apiVersion: "v1" + fieldPath: "spec.nodeName" + - name: "NRI_KUBERNETES_NODEIP" + valueFrom: + fieldRef: + apiVersion: "v1" + fieldPath: "status.hostIP" + + {{- with .Values.controlPlane.extraEnv }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.controlPlane.extraEnvFrom }} + envFrom: {{ toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + - name: nri-kubernetes-config + mountPath: /etc/newrelic-infra/nri-kubernetes.yml + subPath: nri-kubernetes.yml + {{- with .Values.controlPlane.extraVolumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.controlPlane.resources }} + resources: {{ toYaml . | nindent 12 }} + {{- end }} + - name: forwarder + image: {{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.images.forwarder "context" .) }} + imagePullPolicy: {{ .Values.images.forwarder.pullPolicy }} + {{- with include "nriKubernetes.securityContext.container" . | fromYaml }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - containerPort: {{ get (fromYaml (include "nriKubernetes.controlPlane.agentConfig" .)) "http_server_port" }} + env: + - name: "NRIA_LICENSE_KEY" + valueFrom: + secretKeyRef: + name: {{ include "newrelic.common.license.secretName" . }} + key: {{ include "newrelic.common.license.secretKeyName" . }} + + - name: "NRIA_DNS_HOSTNAME_RESOLUTION" + value: "false" + + - name: "K8S_NODE_NAME" + valueFrom: + fieldRef: + apiVersion: "v1" + fieldPath: "spec.nodeName" + + {{- if .Values.useNodeNameAsDisplayName }} + - name: "NRIA_DISPLAY_NAME" + {{- if .Values.prefixDisplayNameWithCluster }} + value: "{{ include "newrelic.common.cluster" . }}:$(K8S_NODE_NAME)" + {{- else }} + valueFrom: + fieldRef: + apiVersion: "v1" + fieldPath: "spec.nodeName" + {{- end }} + {{- end }} + + {{- with .Values.controlPlane.extraEnv }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.controlPlane.extraEnvFrom }} + envFrom: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + - mountPath: /var/db/newrelic-infra/data + name: forwarder-tmpfs-data + - mountPath: /var/db/newrelic-infra/user_data + name: forwarder-tmpfs-user-data + - mountPath: /tmp + name: forwarder-tmpfs-tmp + - name: config + mountPath: /etc/newrelic-infra.yml + subPath: newrelic-infra.yml + {{- with .Values.controlPlane.extraVolumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.controlPlane.resources }} + resources: {{ toYaml . | nindent 12 }} + {{- end }} + volumes: + - name: nri-kubernetes-config + configMap: + name: {{ include "nriKubernetes.controlplane.fullname" . }} + items: + - key: nri-kubernetes.yml + path: nri-kubernetes.yml + - name: forwarder-tmpfs-data + emptyDir: {} + - name: forwarder-tmpfs-user-data + emptyDir: {} + - name: forwarder-tmpfs-tmp + emptyDir: {} + - name: config + configMap: + name: {{ include "nriKubernetes.controlplane.fullname.agent" . }} + items: + - key: newrelic-infra.yml + path: newrelic-infra.yml + {{- with .Values.controlPlane.extraVolumes }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with include "nriKubernetes.controlPlane.affinity" . }} + affinity: + {{- . | nindent 8 }} + {{- end }} + {{- with include "nriKubernetes.controlPlane.tolerations" . }} + tolerations: + {{- . | nindent 8 }} + {{- end }} + nodeSelector: + kubernetes.io/os: linux + {{- with .Values.controlPlane.nodeSelector | default (fromYaml (include "newrelic.common.nodeSelector" .)) }} + {{- toYaml . | nindent 8 }} + {{- end -}} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/rolebinding.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/rolebinding.yaml new file mode 100644 index 0000000000..d97fc181a4 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/rolebinding.yaml @@ -0,0 +1,21 @@ +{{- if .Values.rbac.create }} +{{- $namespaces := (include "nriKubernetes.controlPlane.roleBindingNamespaces" . | fromYaml) -}} +{{- range $namespaces.roleBindingNamespaces }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + {{- include "newrelic.common.labels" $ | nindent 4 }} + name: {{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "nriKubernetes.naming.fullname" $) "suffix" .) }} + namespace: {{ . }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "nriKubernetes.naming.secrets" $ }} +subjects: +- kind: ServiceAccount + name: {{ include "nriKubernetes.controlplane.fullname.serviceAccount" $ }} + namespace: {{ $.Release.Namespace }} +{{- end -}} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/scraper-configmap.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/scraper-configmap.yaml new file mode 100644 index 0000000000..454665deda --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/scraper-configmap.yaml @@ -0,0 +1,36 @@ +{{- if .Values.controlPlane.enabled -}} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "nriKubernetes.controlplane.fullname" . }} + namespace: {{ .Release.Namespace }} +data: + nri-kubernetes.yml: |- + {{- (merge .Values.common.config (include "newrelic.integrationConfigDefaults" . | fromYaml)) | toYaml | nindent 4 }} + controlPlane: + {{- omit .Values.controlPlane.config "etcd" "scheduler" "controllerManager" "apiServer" | toYaml | nindent 6 }} + enabled: true + + {{- if .Values.controlPlane.config.etcd.enabled }} + etcd: + {{- toYaml .Values.controlPlane.config.etcd | nindent 8 -}} + {{- end -}} + + {{- if .Values.controlPlane.config.scheduler.enabled }} + scheduler: + {{- toYaml .Values.controlPlane.config.scheduler | nindent 8 -}} + {{- end -}} + + {{- if .Values.controlPlane.config.controllerManager.enabled }} + controllerManager: + {{- toYaml .Values.controlPlane.config.controllerManager | nindent 8 -}} + {{- end -}} + + {{- if .Values.controlPlane.config.apiServer.enabled }} + apiServer: + {{- toYaml .Values.controlPlane.config.apiServer | nindent 8 -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/serviceaccount.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/serviceaccount.yaml new file mode 100644 index 0000000000..502e1c9865 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/controlplane/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if include "newrelic.common.serviceAccount.create" . -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + {{- with (include "newrelic.common.serviceAccount.annotations" .) }} + annotations: + {{- . | nindent 4 }} + {{- end }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "nriKubernetes.controlplane.fullname.serviceAccount" . }} + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/_affinity_helper.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/_affinity_helper.tpl new file mode 100644 index 0000000000..ce795708d2 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/_affinity_helper.tpl @@ -0,0 +1,14 @@ +{{- /* +As this chart deploys what it should be three charts to maintain the transition to v3 as smooth as possible. +This means that this chart has 3 affinity so a helper should be done per scraper. +*/ -}} +{{- define "nriKubernetes.ksm.affinity" -}} +{{- if or .Values.ksm.affinity .Values.nodeAffinity -}} + {{- $legacyNodeAffinity := fromYaml ( include "newrelic.compatibility.nodeAffinity" . ) | default dict -}} + {{- $valuesAffinity := .Values.ksm.affinity | default dict -}} + {{- $affinity := mustMergeOverwrite $legacyNodeAffinity $valuesAffinity -}} + {{- toYaml $affinity -}} +{{- else if include "newrelic.common.affinity" . -}} + {{- include "newrelic.common.affinity" . -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/_agent-config_helper.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/_agent-config_helper.tpl new file mode 100644 index 0000000000..e7b55644c2 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/_agent-config_helper.tpl @@ -0,0 +1,20 @@ +{{- /* +Defaults for ksm's agent config +*/ -}} +{{- define "nriKubernetes.ksm.agentConfig.defaults" -}} +is_forward_only: true +http_server_enabled: true +http_server_port: 8002 +{{- end -}} + + + +{{- define "nriKubernetes.ksm.agentConfig" -}} +{{- $agentDefaults := fromYaml ( include "newrelic.common.agentConfig.defaults" . ) -}} +{{- $ksm := fromYaml ( include "nriKubernetes.ksm.agentConfig.defaults" . ) -}} +{{- $agentConfig := fromYaml ( include "newrelic.compatibility.agentConfig" . ) -}} +{{- $ksmAgentConfig := .Values.ksm.agentConfig -}} +{{- $customAttributes := dict "custom_attributes" (dict "clusterName" (include "newrelic.common.cluster" . )) -}} + +{{- mustMergeOverwrite $agentDefaults $ksm $agentConfig $ksmAgentConfig $customAttributes | toYaml -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/_host_network.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/_host_network.tpl new file mode 100644 index 0000000000..59a6db7be1 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/_host_network.tpl @@ -0,0 +1,22 @@ +{{/* Returns whether the ksm scraper should run with hostNetwork: true based on the user configuration. */}} +{{- define "nriKubernetes.ksm.hostNetwork" -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if get .Values.ksm "hostNetwork" | kindIs "bool" -}} + {{- if .Values.ksm.hostNetwork -}} + {{- .Values.ksm.hostNetwork -}} + {{- end -}} +{{- else if include "newrelic.common.hostNetwork" . -}} + {{- include "newrelic.common.hostNetwork" . -}} +{{- end -}} +{{- end -}} + + + +{{/* Abstraction of "nriKubernetes.ksm.hostNetwork" that returns true of false directly */}} +{{- define "nriKubernetes.ksm.hostNetwork.value" -}} +{{- if include "nriKubernetes.ksm.hostNetwork" . -}} + true +{{- else -}} + false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/_naming.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/_naming.tpl new file mode 100644 index 0000000000..d8c283c430 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/_naming.tpl @@ -0,0 +1,8 @@ +{{- /* Naming helpers*/ -}} +{{- define "nriKubernetes.ksm.fullname" -}} +{{- include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "nriKubernetes.naming.fullname" .) "suffix" "ksm") -}} +{{- end -}} + +{{- define "nriKubernetes.ksm.fullname.agent" -}} +{{- include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "nriKubernetes.naming.fullname" .) "suffix" "agent-ksm") -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/_tolerations_helper.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/_tolerations_helper.tpl new file mode 100644 index 0000000000..e1a9fd80c2 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/_tolerations_helper.tpl @@ -0,0 +1,11 @@ +{{- /* +As this chart deploys what it should be three charts to maintain the transition to v3 as smooth as possible. +This means that this chart has 3 tolerations so a helper should be done per scraper. +*/ -}} +{{- define "nriKubernetes.ksm.tolerations" -}} +{{- if .Values.ksm.tolerations -}} + {{- toYaml .Values.ksm.tolerations -}} +{{- else if include "newrelic.common.tolerations" . -}} + {{- include "newrelic.common.tolerations" . -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/agent-configmap.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/agent-configmap.yaml new file mode 100644 index 0000000000..6a438e9a3c --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/agent-configmap.yaml @@ -0,0 +1,18 @@ +{{- if .Values.ksm.enabled -}} +{{- if .Values.customAttributes | kindIs "string" }} +{{- fail ( include "newrelic.compatibility.message.customAttributes" . ) -}} +{{- else -}} +apiVersion: v1 +kind: ConfigMap +metadata: + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "nriKubernetes.ksm.fullname.agent" . }} +data: + newrelic-infra.yml: |- + # This is the configuration file for the infrastructure agent. See: + # https://docs.newrelic.com/docs/infrastructure/install-infrastructure-agent/configuration/infrastructure-agent-configuration-settings/ + {{- include "nriKubernetes.ksm.agentConfig" . | nindent 4 }} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/deployment.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/deployment.yaml new file mode 100644 index 0000000000..507199d5a7 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/deployment.yaml @@ -0,0 +1,192 @@ +{{- if include "newrelic.compatibility.ksm.enabled" . -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: {{ .Release.Namespace }} + labels: + {{- include "nriKubernetes.labels" . | nindent 4 }} + name: {{ include "nriKubernetes.ksm.fullname" . }} + {{- $legacyAnnotation:= fromYaml (include "newrelic.compatibility.annotations" .) -}} + {{- with include "newrelic.compatibility.valueWithFallback" (dict "legacy" $legacyAnnotation "supported" .Values.ksm.annotations )}} + annotations: {{ . | nindent 4 }} + {{- end }} +spec: + {{- with .Values.strategy }} + strategy: {{ toYaml . | nindent 4 }} + {{- end }} + selector: + matchLabels: + {{- include "newrelic.common.labels.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: ksm + template: + metadata: + annotations: + checksum/nri-kubernetes: {{ include (print $.Template.BasePath "/ksm/scraper-configmap.yaml") . | sha256sum }} + checksum/agent-config: {{ include (print $.Template.BasePath "/ksm/agent-configmap.yaml") . | sha256sum }} + {{- if include "newrelic.common.license.secret" . }}{{- /* If the is secret to template */}} + checksum/license-secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }} + {{- end }} + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "nriKubernetes.labels.podLabels" . | nindent 8 }} + app.kubernetes.io/component: ksm + spec: + {{- with include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" (list .Values.images.pullSecrets) "context" .) }} + imagePullSecrets: + {{- . | nindent 8 }} + {{- end }} + {{- with include "newrelic.common.dnsConfig" . }} + dnsConfig: + {{- . | nindent 8 }} + {{- end }} + {{- with include "newrelic.common.priorityClassName" . }} + priorityClassName: {{ . }} + {{- end }} + {{- with include "newrelic.common.securityContext.pod" . }} + securityContext: + {{- . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "newrelic.common.serviceAccount.name" . }} + hostNetwork: {{ include "nriKubernetes.ksm.hostNetwork.value" . }} + {{- if include "nriKubernetes.ksm.hostNetwork" . }} + dnsPolicy: ClusterFirstWithHostNet + {{- end }} + + {{- if .Values.ksm.initContainers }} + initContainers: {{- tpl (.Values.ksm.initContainers | toYaml) . | nindent 8 }} + {{- end }} + containers: + - name: ksm + image: {{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.images.integration "context" .) }} + imagePullPolicy: {{ .Values.images.integration.pullPolicy }} + {{- with include "nriKubernetes.securityContext.container" . | fromYaml }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + env: + - name: "NRI_KUBERNETES_SINK_HTTP_PORT" + value: {{ get (fromYaml (include "nriKubernetes.ksm.agentConfig" .)) "http_server_port" | quote }} + - name: "NRI_KUBERNETES_CLUSTERNAME" + value: {{ include "newrelic.common.cluster" . }} + - name: "NRI_KUBERNETES_VERBOSE" + value: {{ include "newrelic.common.verboseLog.valueAsBoolean" . | quote }} + + - name: "NRI_KUBERNETES_NODENAME" + valueFrom: + fieldRef: + apiVersion: "v1" + fieldPath: "spec.nodeName" + + {{- with .Values.ksm.extraEnv }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.ksm.extraEnvFrom }} + envFrom: {{ toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + - name: nri-kubernetes-config + mountPath: /etc/newrelic-infra/nri-kubernetes.yml + subPath: nri-kubernetes.yml + {{- with .Values.ksm.extraVolumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.ksm.resources }} + resources: {{ toYaml . | nindent 12 }} + {{- end }} + - name: forwarder + image: {{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.images.forwarder "context" .) }} + imagePullPolicy: {{ .Values.images.forwarder.pullPolicy }} + {{- with include "nriKubernetes.securityContext.container" . | fromYaml }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - containerPort: {{ get (fromYaml (include "nriKubernetes.ksm.agentConfig" .)) "http_server_port" }} + env: + - name: NRIA_LICENSE_KEY + valueFrom: + secretKeyRef: + name: {{ include "newrelic.common.license.secretName" . }} + key: {{ include "newrelic.common.license.secretKeyName" . }} + + - name: "NRIA_DNS_HOSTNAME_RESOLUTION" + value: "false" + + - name: "K8S_NODE_NAME" + valueFrom: + fieldRef: + apiVersion: "v1" + fieldPath: "spec.nodeName" + + {{- if .Values.useNodeNameAsDisplayName }} + - name: "NRIA_DISPLAY_NAME" + {{- if .Values.prefixDisplayNameWithCluster }} + value: "{{ include "newrelic.common.cluster" . }}:$(K8S_NODE_NAME)" + {{- else }} + valueFrom: + fieldRef: + apiVersion: "v1" + fieldPath: "spec.nodeName" + {{- end }} + {{- end }} + + {{- with .Values.ksm.env }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.ksm.extraEnvFrom }} + envFrom: {{ toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + - mountPath: /var/db/newrelic-infra/data + name: forwarder-tmpfs-data + - mountPath: /var/db/newrelic-infra/user_data + name: forwarder-tmpfs-user-data + - mountPath: /tmp + name: forwarder-tmpfs-tmp + - name: config + mountPath: /etc/newrelic-infra.yml + subPath: newrelic-infra.yml + {{- with .Values.ksm.extraVolumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.ksm.resources }} + resources: {{ toYaml . | nindent 12 }} + {{- end }} + volumes: + - name: nri-kubernetes-config + configMap: + name: {{ include "nriKubernetes.ksm.fullname" . }} + items: + - key: nri-kubernetes.yml + path: nri-kubernetes.yml + - name: forwarder-tmpfs-data + emptyDir: {} + - name: forwarder-tmpfs-user-data + emptyDir: {} + - name: forwarder-tmpfs-tmp + emptyDir: {} + - name: config + configMap: + name: {{ include "nriKubernetes.ksm.fullname.agent" . }} + items: + - key: newrelic-infra.yml + path: newrelic-infra.yml + {{- with .Values.ksm.extraVolumes }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with include "nriKubernetes.ksm.affinity" . }} + affinity: + {{- . | nindent 8 }} + {{- end }} + {{- with include "nriKubernetes.ksm.tolerations" . }} + tolerations: + {{- . | nindent 8 }} + {{- end }} + nodeSelector: + kubernetes.io/os: linux + {{- with .Values.ksm.nodeSelector | default (fromYaml (include "newrelic.common.nodeSelector" .)) }} + {{- toYaml . | nindent 8 }} + {{- end -}} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/scraper-configmap.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/scraper-configmap.yaml new file mode 100644 index 0000000000..3314df9c77 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/ksm/scraper-configmap.yaml @@ -0,0 +1,15 @@ +{{- if include "newrelic.compatibility.ksm.enabled" . -}} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "nriKubernetes.ksm.fullname" . }} + namespace: {{ .Release.Namespace }} +data: + nri-kubernetes.yml: |- + {{- (merge .Values.common.config (include "newrelic.integrationConfigDefaults" . | fromYaml)) | toYaml | nindent 4 }} + ksm: + {{- mustMergeOverwrite .Values.ksm.config (include "newrelic.compatibility.ksm.legacyData" . | fromYaml) | toYaml | nindent 6 -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/_affinity_helper.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/_affinity_helper.tpl new file mode 100644 index 0000000000..a3abf0855e --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/_affinity_helper.tpl @@ -0,0 +1,33 @@ +{{- /* +Patch to add affinity in case we are running in fargate mode +*/ -}} +{{- define "nriKubernetes.kubelet.affinity.fargateDefaults" -}} +nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: eks.amazonaws.com/compute-type + operator: NotIn + values: + - fargate +{{- end -}} + + + +{{- /* +As this chart deploys what it should be three charts to maintain the transition to v3 as smooth as possible. +This means that this chart has 3 affinity so a helper should be done per scraper. +*/ -}} +{{- define "nriKubernetes.kubelet.affinity" -}} + +{{- if or .Values.kubelet.affinity .Values.nodeAffinity -}} + {{- $legacyNodeAffinity := fromYaml ( include "newrelic.compatibility.nodeAffinity" . ) | default dict -}} + {{- $valuesAffinity := .Values.kubelet.affinity | default dict -}} + {{- $affinity := mustMergeOverwrite $legacyNodeAffinity $valuesAffinity -}} + {{- toYaml $affinity -}} +{{- else if include "newrelic.common.affinity" . -}} + {{- include "newrelic.common.affinity" . -}} +{{- else if include "newrelic.fargate" . -}} + {{- include "nriKubernetes.kubelet.affinity.fargateDefaults" . -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/_agent-config_helper.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/_agent-config_helper.tpl new file mode 100644 index 0000000000..ea6ffc25f3 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/_agent-config_helper.tpl @@ -0,0 +1,31 @@ +{{- /* +Defaults for kubelet's agent config +*/ -}} +{{- define "nriKubernetes.kubelet.agentConfig.defaults" -}} +http_server_enabled: true +http_server_port: 8003 +features: + docker_enabled: false +{{- if not ( include "newrelic.common.privileged" . ) }} +is_secure_forward_only: true +{{- end }} +{{- /* +`enableProcessMetrics` is commented in the values and we want to configure it when it is set to something +either `true` or `false`. So we test if the variable is a boolean and in that case simply use it. +*/}} +{{- if (get .Values "enableProcessMetrics" | kindIs "bool") }} +enable_process_metrics: {{ .Values.enableProcessMetrics }} +{{- end }} +{{- end -}} + + + +{{- define "nriKubernetes.kubelet.agentConfig" -}} +{{- $agentDefaults := fromYaml ( include "newrelic.common.agentConfig.defaults" . ) -}} +{{- $kubelet := fromYaml ( include "nriKubernetes.kubelet.agentConfig.defaults" . ) -}} +{{- $agentConfig := fromYaml ( include "newrelic.compatibility.agentConfig" . ) -}} +{{- $kubeletAgentConfig := .Values.kubelet.agentConfig -}} +{{- $customAttributes := dict "custom_attributes" (dict "clusterName" (include "newrelic.common.cluster" . )) -}} + +{{- mustMergeOverwrite $agentDefaults $kubelet $agentConfig $kubeletAgentConfig $customAttributes | toYaml -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/_host_network.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/_host_network.tpl new file mode 100644 index 0000000000..7944f98a72 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/_host_network.tpl @@ -0,0 +1,22 @@ +{{/* Returns whether the kubelet scraper should run with hostNetwork: true based on the user configuration. */}} +{{- define "nriKubernetes.kubelet.hostNetwork" -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if get .Values.kubelet "hostNetwork" | kindIs "bool" -}} + {{- if .Values.kubelet.hostNetwork -}} + {{- .Values.kubelet.hostNetwork -}} + {{- end -}} +{{- else if include "newrelic.common.hostNetwork" . -}} + {{- include "newrelic.common.hostNetwork" . -}} +{{- end -}} +{{- end -}} + + + +{{/* Abstraction of "nriKubernetes.kubelet.hostNetwork" that returns true of false directly */}} +{{- define "nriKubernetes.kubelet.hostNetwork.value" -}} +{{- if include "nriKubernetes.kubelet.hostNetwork" . -}} + true +{{- else -}} + false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/_naming.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/_naming.tpl new file mode 100644 index 0000000000..71c142156d --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/_naming.tpl @@ -0,0 +1,12 @@ +{{- /* Naming helpers*/ -}} +{{- define "nriKubernetes.kubelet.fullname" -}} +{{- include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "nriKubernetes.naming.fullname" .) "suffix" "kubelet") -}} +{{- end -}} + +{{- define "nriKubernetes.kubelet.fullname.agent" -}} +{{- include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "nriKubernetes.naming.fullname" .) "suffix" "agent-kubelet") -}} +{{- end -}} + +{{- define "nriKubernetes.kubelet.fullname.integrations" -}} +{{- include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "nriKubernetes.naming.fullname" .) "suffix" "integrations-cfg") -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/_security_context_helper.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/_security_context_helper.tpl new file mode 100644 index 0000000000..4e334466c1 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/_security_context_helper.tpl @@ -0,0 +1,32 @@ +{{- /*This defines the defaults that the privileged mode has for the agent's securityContext */ -}} +{{- define "nriKubernetes.kubelet.securityContext.privileged" -}} +runAsUser: 0 +runAsGroup: 0 +allowPrivilegeEscalation: true +privileged: true +readOnlyRootFilesystem: true +{{- end -}} + + + +{{- /* This is the container security context for the agent */ -}} +{{- define "nriKubernetes.kubelet.securityContext.agentContainer" -}} +{{- $defaults := dict -}} +{{- if include "newrelic.common.privileged" . -}} +{{- $defaults = fromYaml ( include "nriKubernetes.kubelet.securityContext.privileged" . ) -}} +{{- else -}} +{{- $defaults = fromYaml ( include "nriKubernetes.securityContext.containerDefaults" . ) -}} +{{- end -}} + +{{- $compatibilityLayer := include "newrelic.compatibility.securityContext" . | fromYaml -}} +{{- $commonLibrary := include "newrelic.common.securityContext.container" . | fromYaml -}} + +{{- $finalSecurityContext := dict -}} +{{- if $commonLibrary -}} + {{- $finalSecurityContext = mustMergeOverwrite $commonLibrary $compatibilityLayer -}} +{{- else -}} + {{- $finalSecurityContext = mustMergeOverwrite $defaults $compatibilityLayer -}} +{{- end -}} + +{{- toYaml $finalSecurityContext -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/_tolerations_helper.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/_tolerations_helper.tpl new file mode 100644 index 0000000000..e46d83d69d --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/_tolerations_helper.tpl @@ -0,0 +1,11 @@ +{{- /* +As this chart deploys what it should be three charts to maintain the transition to v3 as smooth as possible. +This means that this chart has 3 tolerations so a helper should be done per scraper. +*/ -}} +{{- define "nriKubernetes.kubelet.tolerations" -}} +{{- if .Values.kubelet.tolerations -}} + {{- toYaml .Values.kubelet.tolerations -}} +{{- else if include "newrelic.common.tolerations" . -}} + {{- include "newrelic.common.tolerations" . -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/agent-configmap.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/agent-configmap.yaml new file mode 100644 index 0000000000..0f71f129ae --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/agent-configmap.yaml @@ -0,0 +1,18 @@ +{{- if .Values.kubelet.enabled -}} +{{- if .Values.customAttributes | kindIs "string" }} +{{- fail ( include "newrelic.compatibility.message.customAttributes" . ) -}} +{{- else -}} +apiVersion: v1 +kind: ConfigMap +metadata: + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "nriKubernetes.kubelet.fullname.agent" . }} +data: + newrelic-infra.yml: |- + # This is the configuration file for the infrastructure agent. See: + # https://docs.newrelic.com/docs/infrastructure/install-infrastructure-agent/configuration/infrastructure-agent-configuration-settings/ + {{- include "nriKubernetes.kubelet.agentConfig" . | nindent 4 }} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/daemonset.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/daemonset.yaml new file mode 100644 index 0000000000..517079be76 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/daemonset.yaml @@ -0,0 +1,265 @@ +{{- if (.Values.kubelet.enabled) }} +apiVersion: apps/v1 +kind: DaemonSet +metadata: + namespace: {{ .Release.Namespace }} + labels: + {{- include "nriKubernetes.labels" . | nindent 4 }} + name: {{ include "nriKubernetes.kubelet.fullname" . }} + {{- $legacyAnnotation:= fromYaml (include "newrelic.compatibility.annotations" .) -}} + {{- with include "newrelic.compatibility.valueWithFallback" (dict "legacy" $legacyAnnotation "supported" .Values.kubelet.annotations )}} + annotations: {{ . | nindent 4 }} + {{- end }} +spec: + {{- with .Values.updateStrategy }} + updateStrategy: {{ toYaml . | nindent 4 }} + {{- end }} + selector: + matchLabels: + {{- include "newrelic.common.labels.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: kubelet + template: + metadata: + annotations: + checksum/nri-kubernetes: {{ include (print $.Template.BasePath "/kubelet/scraper-configmap.yaml") . | sha256sum }} + checksum/agent-config: {{ include (print $.Template.BasePath "/kubelet/agent-configmap.yaml") . | sha256sum }} + {{- if include "newrelic.common.license.secret" . }}{{- /* If the is secret to template */}} + checksum/license-secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }} + {{- end }} + checksum/integrations_config: {{ include (print $.Template.BasePath "/kubelet/integrations-configmap.yaml") . | sha256sum }} + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "nriKubernetes.labels.podLabels" . | nindent 8 }} + app.kubernetes.io/component: kubelet + spec: + {{- with include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" (list .Values.images.pullSecrets) "context" .) }} + imagePullSecrets: + {{- . | nindent 8 }} + {{- end }} + {{- with include "newrelic.common.dnsConfig" . }} + dnsConfig: + {{- . | nindent 8 }} + {{- end }} + {{- with include "newrelic.common.priorityClassName" . }} + priorityClassName: {{ . }} + {{- end }} + {{- with include "newrelic.common.securityContext.pod" . }} + securityContext: + {{- . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "newrelic.common.serviceAccount.name" . }} + hostNetwork: {{ include "nriKubernetes.kubelet.hostNetwork.value" . }} + {{- if include "nriKubernetes.kubelet.hostNetwork" . }} + dnsPolicy: ClusterFirstWithHostNet + {{- end }} + + {{- if .Values.kubelet.initContainers }} + initContainers: {{- tpl (.Values.kubelet.initContainers | toYaml) . | nindent 8 }} + {{- end }} + containers: + - name: kubelet + image: {{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.images.integration "context" .) }} + imagePullPolicy: {{ .Values.images.integration.pullPolicy }} + {{- with include "nriKubernetes.securityContext.container" . | fromYaml }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + env: + - name: "NRI_KUBERNETES_SINK_HTTP_PORT" + value: {{ get (fromYaml (include "nriKubernetes.kubelet.agentConfig" .)) "http_server_port" | quote }} + - name: "NRI_KUBERNETES_CLUSTERNAME" + value: {{ include "newrelic.common.cluster" . }} + - name: "NRI_KUBERNETES_VERBOSE" + value: {{ include "newrelic.common.verboseLog.valueAsBoolean" . | quote }} + + - name: "NRI_KUBERNETES_NODENAME" + valueFrom: + fieldRef: + apiVersion: "v1" + fieldPath: "spec.nodeName" + # Required to connect to the kubelet + - name: "NRI_KUBERNETES_NODEIP" + valueFrom: + fieldRef: + apiVersion: "v1" + fieldPath: "status.hostIP" + + {{- with .Values.kubelet.extraEnv }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.kubelet.extraEnvFrom }} + envFrom: {{ toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + - name: nri-kubernetes-config + mountPath: /etc/newrelic-infra/nri-kubernetes.yml + subPath: nri-kubernetes.yml + {{- with .Values.kubelet.extraVolumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.kubelet.resources }} + resources: {{ toYaml . | nindent 12 }} + {{- end }} + - name: agent + image: {{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.images.agent "context" .) }} + args: [ "newrelic-infra" ] + imagePullPolicy: {{ .Values.images.agent.pullPolicy }} + {{- with include "nriKubernetes.kubelet.securityContext.agentContainer" . | fromYaml }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - containerPort: {{ get (fromYaml (include "nriKubernetes.kubelet.agentConfig" .)) "http_server_port" }} + env: + - name: NRIA_LICENSE_KEY + valueFrom: + secretKeyRef: + name: {{ include "newrelic.common.license.secretName" . }} + key: {{ include "newrelic.common.license.secretKeyName" . }} + + - name: "NRIA_OVERRIDE_HOSTNAME_SHORT" + valueFrom: + fieldRef: + apiVersion: "v1" + fieldPath: "spec.nodeName" + + - name: "NRIA_OVERRIDE_HOSTNAME" + valueFrom: + fieldRef: + apiVersion: "v1" + fieldPath: "spec.nodeName" + + {{- if not (include "newrelic.common.privileged" .) }} + # Override NRIA_OVERRIDE_HOST_ROOT to empty if unprivileged. This must be done as an env var as the + # `k8s-events-forwarder` and `infrastructure-bundle` images ship this very same env var set to /host. + - name: "NRIA_OVERRIDE_HOST_ROOT" + value: "" + {{- end }} + + - name: "NRI_KUBERNETES_NODE_NAME" + valueFrom: + fieldRef: + apiVersion: "v1" + fieldPath: "spec.nodeName" + + {{- if .Values.useNodeNameAsDisplayName }} + - name: "NRIA_DISPLAY_NAME" + {{- if .Values.prefixDisplayNameWithCluster }} + value: "{{ include "newrelic.common.cluster" . }}:$(NRI_KUBERNETES_NODE_NAME)" + {{- else }} + valueFrom: + fieldRef: + apiVersion: "v1" + fieldPath: "spec.nodeName" + {{- end }} + {{- end }} + + {{- /* Needed to populate clustername in integration metrics */}} + - name: "CLUSTER_NAME" + value: {{ include "newrelic.common.cluster" . }} + - name: "NRIA_PASSTHROUGH_ENVIRONMENT" + value: "CLUSTER_NAME" + + {{- /* Needed for autodiscovery since hostNetwork=false */}} + - name: "NRIA_HOST" + valueFrom: + fieldRef: + apiVersion: "v1" + fieldPath: "status.hostIP" + + {{- with .Values.kubelet.extraEnv }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.kubelet.extraEnvFrom }} + envFrom: {{ toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + - name: config + mountPath: /etc/newrelic-infra.yml + subPath: newrelic-infra.yml + - name: nri-integrations-cfg-volume + mountPath: /etc/newrelic-infra/integrations.d/ + {{- if include "newrelic.common.privileged" . }} + - name: dev + mountPath: /dev + - name: host-containerd-socket + mountPath: /run/containerd/containerd.sock + - name: host-docker-socket + mountPath: /var/run/docker.sock + - name: log + mountPath: /var/log + - name: host-volume + mountPath: /host + mountPropagation: HostToContainer + readOnly: true + {{- end }} + - mountPath: /var/db/newrelic-infra/data + name: agent-tmpfs-data + - mountPath: /var/db/newrelic-infra/user_data + name: agent-tmpfs-user-data + - mountPath: /tmp + name: agent-tmpfs-tmp + {{- with .Values.kubelet.extraVolumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.kubelet.resources }} + resources: {{ toYaml . | nindent 12 }} + {{- end }} + volumes: + {{- if include "newrelic.common.privileged" . }} + - name: dev + hostPath: + path: /dev + - name: host-containerd-socket + hostPath: + path: /run/containerd/containerd.sock + - name: host-docker-socket + hostPath: + path: /var/run/docker.sock + - name: log + hostPath: + path: /var/log + - name: host-volume + hostPath: + path: / + {{- end }} + - name: agent-tmpfs-data + emptyDir: {} + - name: agent-tmpfs-user-data + emptyDir: {} + - name: agent-tmpfs-tmp + emptyDir: {} + - name: nri-kubernetes-config + configMap: + name: {{ include "nriKubernetes.kubelet.fullname" . }} + items: + - key: nri-kubernetes.yml + path: nri-kubernetes.yml + - name: config + configMap: + name: {{ include "nriKubernetes.kubelet.fullname.agent" . }} + items: + - key: newrelic-infra.yml + path: newrelic-infra.yml + - name: nri-integrations-cfg-volume + configMap: + name: {{ include "nriKubernetes.kubelet.fullname.integrations" . }} + {{- with .Values.kubelet.extraVolumes }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with include "nriKubernetes.kubelet.affinity" . }} + affinity: + {{- . | nindent 8 }} + {{- end }} + {{- with include "nriKubernetes.kubelet.tolerations" . }} + tolerations: + {{- . | nindent 8 }} + {{- end }} + nodeSelector: + kubernetes.io/os: linux + {{- with .Values.kubelet.nodeSelector | default (fromYaml (include "newrelic.common.nodeSelector" .)) }} + {{- toYaml . | nindent 8 }} + {{- end -}} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/integrations-configmap.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/integrations-configmap.yaml new file mode 100644 index 0000000000..abf381f383 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/integrations-configmap.yaml @@ -0,0 +1,72 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "nriKubernetes.kubelet.fullname.integrations" . }} +data: + # This ConfigMap holds config files for integrations. They should have the following format: + #redis-config.yml: | + # # Run auto discovery to find pods with label "app=redis" + # discovery: + # command: + # # Run discovery for Kubernetes. Use the following optional arguments: + # # --namespaces: Comma separated list of namespaces to discover pods on + # # --tls: Use secure (TLS) connection + # # --port: Port used to connect to the kubelet. Default is 10255 + # exec: /var/db/newrelic-infra/nri-discovery-kubernetes --port PORT --tls + # match: + # label.app: redis + # integrations: + # - name: nri-redis + # env: + # # using the discovered IP as the hostname address + # HOSTNAME: ${discovery.ip} + # PORT: 6379 + # KEYS: '{"0":[""],"1":[""]}' + # REMOTE_MONITORING: true + # labels: + # env: production + {{- if .Values.integrations -}} + {{- range $k, $v := .Values.integrations -}} + {{- $k | trimSuffix ".yaml" | trimSuffix ".yml" | nindent 2 -}}.yaml: |- + {{- $v | toYaml | nindent 4 -}} + {{- end }} + {{- end }} + + {{- /* This template will add and template the integrations in the old .Values.integrations_config */}} + {{- include "newrelic.compatibility.integrations" . | nindent 2 }} + + {{- /* This template will add Pixie Health check to the integrations */}} + {{- if .Values.selfMonitoring.pixie.enabled }} + pixie-health-check.yaml: | + --- + # This Flex config performs periodic checks of the Pixie + # /healthz and /statusz endpoints exposed by the Pixie Cloud Connector. + # A status for each endpoint is sent to New Relic in a pixieHealthCheck event. + # + # If Pixie is not installed in the cluster, no events will be generated. + # This can also be disabled with enablePixieHealthCheck: false in the values.yaml file. + discovery: + command: + exec: /var/db/newrelic-infra/nri-discovery-kubernetes --tls --port 10250 + match: + label.name: vizier-cloud-connector + integrations: + - name: nri-flex + interval: 60s + config: + name: pixie-health-check + apis: + - event_type: pixieHealth + commands: + - run: curl --insecure -s https://${discovery.ip}:50800/healthz | xargs | awk '{print "cloud_connector_health:"$1}' + split_by: ":" + merge: pixieHealthCheck + - event_type: pixieStatus + commands: + - run: curl --insecure -s https://${discovery.ip}:50800/statusz | awk '{if($1 == ""){ print "cloud_connector_status:OK" } else { print "cloud_connector_status:"$1 }}' + split_by: ":" + merge: pixieHealthCheck + {{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/scraper-configmap.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/scraper-configmap.yaml new file mode 100644 index 0000000000..e43b5227fc --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/kubelet/scraper-configmap.yaml @@ -0,0 +1,18 @@ +{{- if .Values.kubelet.enabled -}} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "nriKubernetes.kubelet.fullname" . }} + namespace: {{ .Release.Namespace }} +data: + nri-kubernetes.yml: | + {{- (merge .Values.common.config (include "newrelic.integrationConfigDefaults" . | fromYaml)) | toYaml | nindent 4 }} + kubelet: + enabled: true + {{- if .Values.kubelet.config }} + {{- toYaml .Values.kubelet.config | nindent 6 }} + {{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/podsecuritypolicy.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/podsecuritypolicy.yaml new file mode 100644 index 0000000000..5b5058511b --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/podsecuritypolicy.yaml @@ -0,0 +1,26 @@ +{{- if .Values.rbac.pspEnabled }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: privileged-{{ include "newrelic.common.naming.fullname" . }} +spec: + allowedCapabilities: + - '*' + fsGroup: + rule: RunAsAny + privileged: true + runAsUser: + rule: RunAsAny + seLinux: + rule: RunAsAny + supplementalGroups: + rule: RunAsAny + volumes: + - '*' + hostPID: true + hostIPC: true + hostNetwork: true + hostPorts: + - min: 1 + max: 65536 +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/secret.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/secret.yaml new file mode 100644 index 0000000000..f558ee86c9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/secret.yaml @@ -0,0 +1,2 @@ +{{- /* Common library will take care of creating the secret or not. */}} +{{- include "newrelic.common.license.secret" . }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/serviceaccount.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/serviceaccount.yaml new file mode 100644 index 0000000000..f987cc5124 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if include "newrelic.common.serviceAccount.create" . -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + {{- with (include "newrelic.common.serviceAccount.annotations" .) }} + annotations: + {{- . | nindent 4 }} + {{- end }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "newrelic.common.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/values.yaml new file mode 100644 index 0000000000..56320a167d --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-infrastructure/values.yaml @@ -0,0 +1,602 @@ +# -- Override the name of the chart +nameOverride: "" +# -- Override the full name of the release +fullnameOverride: "" + +# -- Name of the Kubernetes cluster monitored. Can be configured also with `global.cluster` +cluster: "" +# -- This set this license key to use. Can be configured also with `global.licenseKey` +licenseKey: "" +# -- In case you don't want to have the license key in you values, this allows you to point to a user created secret to get the key from there. Can be configured also with `global.customSecretName` +customSecretName: "" +# -- In case you don't want to have the license key in you values, this allows you to point to which secret key is the license key located. Can be configured also with `global.customSecretLicenseKey` +customSecretLicenseKey: "" + +# -- Images used by the chart for the integration and agents. +# @default -- See `values.yaml` +images: + # -- The secrets that are needed to pull images from a custom registry. + pullSecrets: [] + # - name: regsecret + # -- Image for the New Relic Infrastructure Agent sidecar. + # @default -- See `values.yaml` + forwarder: + registry: "" + repository: newrelic/k8s-events-forwarder + tag: 1.55.2 + pullPolicy: IfNotPresent + # -- Image for the New Relic Infrastructure Agent plus integrations. + # @default -- See `values.yaml` + agent: + registry: "" + repository: newrelic/infrastructure-bundle + tag: 3.2.49 + pullPolicy: IfNotPresent + # -- Image for the New Relic Kubernetes integration. + # @default -- See `values.yaml` + integration: + registry: "" + repository: newrelic/nri-kubernetes + tag: + pullPolicy: IfNotPresent + +# -- Config that applies to all instances of the solution: kubelet, ksm, control plane and sidecars. +# @default -- See `values.yaml` +common: + # Configuration entries that apply to all instances of the integration: kubelet, ksm and control plane. + config: + # common.config.interval -- (duration) Intervals larger than 40s are not supported and will cause the NR UI to not + # behave properly. Any non-nil value will override the `lowDataMode` default. + # @default -- `15s` (See [Low data mode](README.md#low-data-mode)) + interval: + # -- Config for filtering ksm and kubelet metrics by namespace. + namespaceSelector: {} + # If you want to include only namespaces with a given label you could do so by adding: + # matchLabels: + # newrelic.com/scrape: true + # Otherwise you can build more complex filters and include or exclude certain namespaces by adding one or multiple + # expressions that are added, for instance: + # matchExpressions: + # - {key: newrelic.com/scrape, operator: NotIn, values: ["false"]} + + # -- Config for the Infrastructure agent. + # Will be used by the forwarder sidecars and the agent running integrations. + # See: https://docs.newrelic.com/docs/infrastructure/install-infrastructure-agent/configuration/infrastructure-agent-configuration-settings/ + agentConfig: {} + +# lowDataMode -- (bool) Send less data by incrementing the interval from `15s` (the default when `lowDataMode` is `false` or `nil`) to `30s`. +# Non-nil values of `common.config.interval` will override this value. +# @default -- `false` (See [Low data mode](README.md#low-data-mode)) +lowDataMode: + +# sink - Configuration for the scraper sink. +sink: + http: + # -- The amount of time the scraper container to probe infra agent sidecar container before giving up and restarting during pod starts. + probeTimeout: 90s + # -- The amount of time the scraper container to backoff when it fails to probe infra agent sidecar. + probeBackoff: 5s + +# kubelet -- Configuration for the DaemonSet that collects metrics from the Kubelet. +# @default -- See `values.yaml` +kubelet: + # -- Enable kubelet monitoring. + # Advanced users only. Setting this to `false` is not supported and will break the New Relic experience. + enabled: true + annotations: {} + # -- Tolerations for the control plane DaemonSet. + # @default -- Schedules in all tainted nodes + tolerations: + - operator: "Exists" + effect: "NoSchedule" + - operator: "Exists" + effect: "NoExecute" + nodeSelector: {} + # -- (bool) Sets pod's hostNetwork. When set bypasses global/common variable + # @default -- Not set + hostNetwork: + affinity: {} + # -- Config for the Infrastructure agent that will forward the metrics to the backend and will run the integrations in this cluster. + # It will be merged with the configuration in `.common.agentConfig`. You can see all the agent configurations in + # [New Relic docs](https://docs.newrelic.com/docs/infrastructure/install-infrastructure-agent/configuration/infrastructure-agent-configuration-settings/) + # e.g. you can set `passthrough_environment` int the [config file](https://docs.newrelic.com/docs/infrastructure/install-infrastructure-agent/configuration/configure-infrastructure-agent/#config-file) + # so the agent let use that environment variables to the integrations. + agentConfig: {} + # passthrough_environment: + # - A_ENVIRONMENT_VARIABLE_SET_IN_extraEnv + # - A_ENVIRONMENT_VARIABLE_SET_IN_A_CONFIG_MAP_SET_IN_entraEnvForm + + # -- Add user environment variables to the agent + extraEnv: [] + # -- Add user environment from configMaps or secrets as variables to the agent + extraEnvFrom: [] + # -- Volumes to mount in the containers + extraVolumes: [] + # -- Defines where to mount volumes specified with `extraVolumes` + extraVolumeMounts: [] + initContainers: [] + resources: + limits: + memory: 300M + requests: + cpu: 100m + memory: 150M + config: + # -- Timeout for the kubelet APIs contacted by the integration + timeout: 10s + # -- Number of retries after timeout expired + retries: 3 + # -- Max number of scraper rerun when scraper runtime error happens + scraperMaxReruns: 4 + # port: + # scheme: + +# ksm -- Configuration for the Deployment that collects state metrics from KSM (kube-state-metrics). +# @default -- See `values.yaml` +ksm: + # -- Enable cluster state monitoring. + # Advanced users only. Setting this to `false` is not supported and will break the New Relic experience. + enabled: true + annotations: {} + # -- Tolerations for the KSM Deployment. + # @default -- Schedules in all tainted nodes + tolerations: + - operator: "Exists" + effect: "NoSchedule" + - operator: "Exists" + effect: "NoExecute" + nodeSelector: {} + # -- (bool) Sets pod's hostNetwork. When set bypasses global/common variable + # @default -- Not set + hostNetwork: + # -- Affinity for the KSM Deployment. + # @default -- Deployed in the same node as KSM + affinity: + podAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + topologyKey: kubernetes.io/hostname + labelSelector: + matchLabels: + app.kubernetes.io/name: kube-state-metrics + weight: 100 + # -- Config for the Infrastructure agent that will forward the metrics to the backend. It will be merged with the configuration in `.common.agentConfig` + # See: https://docs.newrelic.com/docs/infrastructure/install-infrastructure-agent/configuration/infrastructure-agent-configuration-settings/ + agentConfig: {} + extraEnv: [] + extraEnvFrom: [] + extraVolumes: [] + extraVolumeMounts: [] + initContainers: [] + # -- Resources for the KSM scraper pod. + # Keep in mind that sharding is not supported at the moment, so memory usage for this component ramps up quickly on + # large clusters. + # @default -- 100m/150M -/850M + resources: + limits: + memory: 850M # Bump me up if KSM pod shows restarts. + requests: + cpu: 100m + memory: 150M + config: + # -- Timeout for the ksm API contacted by the integration + timeout: 10s + # -- Number of retries after timeout expired + retries: 3 + # -- if specified autodiscovery is not performed and the specified URL is used + # staticUrl: "http://test.io:8080/metrics" + # -- Label selector that will be used to automatically discover an instance of kube-state-metrics running in the cluster. + selector: "app.kubernetes.io/name=kube-state-metrics" + # -- Scheme to use to connect to kube-state-metrics. Supported values are `http` and `https`. + scheme: "http" + # -- Restrict autodiscovery of the kube-state-metrics endpoint to those using a specific port. If empty or `0`, all endpoints are considered regardless of their port (recommended). + # port: 8080 + # -- Restrict autodiscovery of the kube-state-metrics service to a particular namespace. + # @default -- All namespaces are searched (recommended). + # namespace: "ksm-namespace" + +# controlPlane -- Configuration for the control plane scraper. +# @default -- See `values.yaml` +controlPlane: + # -- Deploy control plane monitoring component. + enabled: true + annotations: {} + # -- Tolerations for the control plane DaemonSet. + # @default -- Schedules in all tainted nodes + tolerations: + - operator: "Exists" + effect: "NoSchedule" + - operator: "Exists" + effect: "NoExecute" + nodeSelector: {} + # -- Affinity for the control plane DaemonSet. + # @default -- Deployed only in master nodes. + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: node-role.kubernetes.io/control-plane + operator: Exists + - matchExpressions: + - key: node-role.kubernetes.io/controlplane + operator: Exists + - matchExpressions: + - key: node-role.kubernetes.io/etcd + operator: Exists + - matchExpressions: + - key: node-role.kubernetes.io/master + operator: Exists + # -- How to deploy the control plane scraper. If autodiscovery is in use, it should be `DaemonSet`. + # Advanced users using static endpoints set this to `Deployment` to avoid reporting metrics twice. + kind: DaemonSet + # -- Run Control Plane scraper with `hostNetwork`. + # `hostNetwork` is required for most control plane configurations, as they only accept connections from localhost. + hostNetwork: true + # -- Config for the Infrastructure agent that will forward the metrics to the backend. It will be merged with the configuration in `.common.agentConfig` + # See: https://docs.newrelic.com/docs/infrastructure/install-infrastructure-agent/configuration/infrastructure-agent-configuration-settings/ + agentConfig: {} + extraEnv: [] + extraEnvFrom: [] + extraVolumes: [] + extraVolumeMounts: [] + initContainers: [] + resources: + limits: + memory: 300M + requests: + cpu: 100m + memory: 150M + config: + # -- Timeout for the Kubernetes APIs contacted by the integration + timeout: 10s + # -- Number of retries after timeout expired + retries: 3 + # -- etcd monitoring configuration + # @default -- Common settings for most K8s distributions. + etcd: + # -- Enable etcd monitoring. Might require manual configuration in some environments. + enabled: true + # Discover etcd pods using the following namespaces and selectors. + # If a pod matches the selectors, the scraper will attempt to reach it through the `endpoints` defined below. + autodiscover: + - selector: "tier=control-plane,component=etcd" + namespace: kube-system + # Set to true to consider only pods sharing the node with the scraper pod. + # This should be set to `true` if Kind is Daemonset, `false` otherwise. + matchNode: true + # Try to reach etcd using the following endpoints. + endpoints: + - url: https://localhost:4001 + insecureSkipVerify: true + auth: + type: bearer + - url: http://localhost:2381 + - selector: "k8s-app=etcd-manager-main" + namespace: kube-system + matchNode: true + endpoints: + - url: https://localhost:4001 + insecureSkipVerify: true + auth: + type: bearer + - selector: "k8s-app=etcd" + namespace: kube-system + matchNode: true + endpoints: + - url: https://localhost:4001 + insecureSkipVerify: true + auth: + type: bearer + # Openshift users might want to remove previous autodiscover entries and add this one instead. + # Manual steps are required to create a secret containing the required TLS certificates to connect to etcd. + # - selector: "app=etcd,etcd=true,k8s-app=etcd" + # namespace: openshift-etcd + # matchNode: true + # endpoints: + # - url: https://localhost:9979 + # insecureSkipVerify: true + # auth: + # type: mTLS + # mtls: + # secretName: secret-name + # secretNamespace: secret-namespace + + # -- staticEndpoint configuration. + # It is possible to specify static endpoint to scrape. If specified 'autodiscover' section is ignored. + # If set the static endpoint should be reachable, otherwise an error will be returned and the integration stops. + # Notice that if deployed as a daemonSet and not as a Deployment setting static URLs could lead to duplicate data + # staticEndpoint: + # url: https://url:port + # insecureSkipVerify: true + # auth: {} + + # -- Scheduler monitoring configuration + # @default -- Common settings for most K8s distributions. + scheduler: + # -- Enable scheduler monitoring. + enabled: true + autodiscover: + - selector: "tier=control-plane,component=kube-scheduler" + namespace: kube-system + matchNode: true + endpoints: + - url: https://localhost:10259 + insecureSkipVerify: true + auth: + type: bearer + - selector: "k8s-app=kube-scheduler" + namespace: kube-system + matchNode: true + endpoints: + - url: https://localhost:10259 + insecureSkipVerify: true + auth: + type: bearer + - selector: "app=openshift-kube-scheduler,scheduler=true" + namespace: openshift-kube-scheduler + matchNode: true + endpoints: + - url: https://localhost:10259 + insecureSkipVerify: true + auth: + type: bearer + - selector: "app=openshift-kube-scheduler,scheduler=true" + namespace: kube-system + matchNode: true + endpoints: + - url: https://localhost:10259 + insecureSkipVerify: true + auth: + type: bearer + # -- staticEndpoint configuration. + # It is possible to specify static endpoint to scrape. If specified 'autodiscover' section is ignored. + # If set the static endpoint should be reachable, otherwise an error will be returned and the integration stops. + # Notice that if deployed as a daemonSet and not as a Deployment setting static URLs could lead to duplicate data + # staticEndpoint: + # url: https://url:port + # insecureSkipVerify: true + # auth: {} + + # -- Controller manager monitoring configuration + # @default -- Common settings for most K8s distributions. + controllerManager: + # -- Enable controller manager monitoring. + enabled: true + autodiscover: + - selector: "tier=control-plane,component=kube-controller-manager" + namespace: kube-system + matchNode: true + endpoints: + - url: https://localhost:10257 + insecureSkipVerify: true + auth: + type: bearer + - selector: "k8s-app=kube-controller-manager" + namespace: kube-system + matchNode: true + endpoints: + - url: https://localhost:10257 + insecureSkipVerify: true + auth: + type: bearer + - selector: "app=kube-controller-manager,kube-controller-manager=true" + namespace: openshift-kube-controller-manager + matchNode: true + endpoints: + - url: https://localhost:10257 + insecureSkipVerify: true + auth: + type: bearer + - selector: "app=kube-controller-manager,kube-controller-manager=true" + namespace: kube-system + matchNode: true + endpoints: + - url: https://localhost:10257 + insecureSkipVerify: true + auth: + type: bearer + - selector: "app=controller-manager,controller-manager=true" + namespace: kube-system + matchNode: true + endpoints: + - url: https://localhost:10257 + insecureSkipVerify: true + auth: + type: bearer + # mtls: + # secretName: secret-name + # secretNamespace: secret-namespace + # -- staticEndpoint configuration. + # It is possible to specify static endpoint to scrape. If specified 'autodiscover' section is ignored. + # If set the static endpoint should be reachable, otherwise an error will be returned and the integration stops. + # Notice that if deployed as a daemonSet and not as a Deployment setting static URLs could lead to duplicate data + # staticEndpoint: + # url: https://url:port + # insecureSkipVerify: true + # auth: {} + + # -- API Server monitoring configuration + # @default -- Common settings for most K8s distributions. + apiServer: + # -- Enable API Server monitoring + enabled: true + autodiscover: + - selector: "tier=control-plane,component=kube-apiserver" + namespace: kube-system + matchNode: true + endpoints: + - url: https://localhost:8443 + insecureSkipVerify: true + auth: + type: bearer + # Endpoint distributions target: Kind(v1.22.1) + - url: https://localhost:6443 + insecureSkipVerify: true + auth: + type: bearer + - url: http://localhost:8080 + - selector: "k8s-app=kube-apiserver" + namespace: kube-system + matchNode: true + endpoints: + - url: https://localhost:8443 + insecureSkipVerify: true + auth: + type: bearer + - url: http://localhost:8080 + - selector: "app=openshift-kube-apiserver,apiserver=true" + namespace: openshift-kube-apiserver + matchNode: true + endpoints: + - url: https://localhost:8443 + insecureSkipVerify: true + auth: + type: bearer + - url: https://localhost:6443 + insecureSkipVerify: true + auth: + type: bearer + - selector: "app=openshift-kube-apiserver,apiserver=true" + namespace: kube-system + matchNode: true + endpoints: + - url: https://localhost:8443 + insecureSkipVerify: true + auth: + type: bearer + # -- staticEndpoint configuration. + # It is possible to specify static endpoint to scrape. If specified 'autodiscover' section is ignored. + # If set the static endpoint should be reachable, otherwise an error will be returned and the integration stops. + # Notice that if deployed as a daemonSet and not as a Deployment setting static URLs could lead to duplicate data + # staticEndpoint: + # url: https://url:port + # insecureSkipVerify: true + # auth: {} + +# -- Update strategy for the deployed DaemonSets. +# @default -- See `values.yaml` +updateStrategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 + +# -- Update strategy for the deployed Deployments. +# @default -- `type: Recreate` +strategy: + type: Recreate + +# -- Adds extra attributes to the cluster and all the metrics emitted to the backend. Can be configured also with `global.customAttributes` +customAttributes: {} + +# -- Settings controlling ServiceAccount creation. +# @default -- See `values.yaml` +serviceAccount: + # -- (bool) Whether the chart should automatically create the ServiceAccount objects required to run. + # @default -- `true` + create: + annotations: {} + # If not set and create is true, a name is generated using the fullname template + name: "" + +# -- Additional labels for chart objects. Can be configured also with `global.labels` +labels: {} +# -- Annotations to be added to all pods created by the integration. +podAnnotations: {} +# -- Additional labels for chart pods. Can be configured also with `global.podLabels` +podLabels: {} + +# -- Run the integration with full access to the host filesystem and network. +# Running in this mode allows reporting fine-grained cpu, memory, process and network metrics for your nodes. +privileged: true +# -- Sets pod's priorityClassName. Can be configured also with `global.priorityClassName` +priorityClassName: "" +# -- (bool) Sets pod's hostNetwork. Can be configured also with `global.hostNetwork` +# @default -- `false` +hostNetwork: +# -- Sets security context (at pod level). Can be configured also with `global.podSecurityContext` +podSecurityContext: {} +# -- Sets security context (at container level). Can be configured also with `global.containerSecurityContext` +containerSecurityContext: {} + +# -- Sets pod's dnsConfig. Can be configured also with `global.dnsConfig` +dnsConfig: {} + +# Settings controlling RBAC objects creation. +rbac: + # rbac.create -- Whether the chart should automatically create the RBAC objects required to run. + create: true + # rbac.pspEnabled -- Whether the chart should create Pod Security Policy objects. + pspEnabled: false + +# -- Sets pod/node affinities set almost globally. (See [Affinities and tolerations](README.md#affinities-and-tolerations)) +affinity: {} +# -- Sets pod's node selector almost globally. (See [Affinities and tolerations](README.md#affinities-and-tolerations)) +nodeSelector: {} +# -- Sets pod's tolerations to node taints almost globally. (See [Affinities and tolerations](README.md#affinities-and-tolerations)) +tolerations: [] + +# -- Config files for other New Relic integrations that should run in this cluster. +integrations: {} +# If you wish to monitor services running on Kubernetes you can provide integrations +# configuration under `integrations`. You just need to create a new entry where +# the key is the filename of the configuration file and the value is the content of +# the integration configuration. +# The data is the actual integration configuration as described in the spec here: +# https://docs.newrelic.com/docs/integrations/integrations-sdk/file-specifications/integration-configuration-file-specifications-agent-v180 +# For example, if you wanted to monitor a Redis instance that has a label "app=sampleapp" +# you could do so by adding following entry: +# nri-redis-sampleapp: +# discovery: +# command: +# # Run NRI Discovery for Kubernetes +# # https://github.com/newrelic/nri-discovery-kubernetes +# exec: /var/db/newrelic-infra/nri-discovery-kubernetes +# match: +# label.app: sampleapp +# integrations: +# - name: nri-redis +# env: +# # using the discovered IP as the hostname address +# HOSTNAME: ${discovery.ip} +# PORT: 6379 +# labels: +# env: test + +# -- (bool) Collect detailed metrics from processes running in the host. +# This defaults to true for accounts created before July 20, 2020. +# ref: https://docs.newrelic.com/docs/release-notes/infrastructure-release-notes/infrastructure-agent-release-notes/new-relic-infrastructure-agent-1120 +# @default -- `false` +enableProcessMetrics: + +# Prefix nodes display name with cluster to reduce chances of collisions +# prefixDisplayNameWithCluster: false + +# 'true' will use the node name as the name for the "host", +# note that it may cause data collision if the node name is the same in different clusters +# and prefixDisplayNameWithCluster is not set to true. +# 'false' will use the host name as the name for the "host". +# useNodeNameAsDisplayName: true + +selfMonitoring: + pixie: + # selfMonitoring.pixie.enabled -- Enables the Pixie Health Check nri-flex config. + # This Flex config performs periodic checks of the Pixie /healthz and /statusz endpoints exposed by the Pixie + # Cloud Connector. A status for each endpoint is sent to New Relic in a pixieHealthCheck event. + enabled: false + + +# -- Configures the integration to send all HTTP/HTTPS request through the proxy in that URL. The URL should have a standard format like `https://user:password@hostname:port`. Can be configured also with `global.proxy` +proxy: "" + +# -- (bool) Send the metrics to the staging backend. Requires a valid staging license key. Can be configured also with `global.nrStaging` +# @default -- `false` +nrStaging: +fedramp: + # -- (bool) Enables FedRAMP. Can be configured also with `global.fedramp.enabled` + # @default -- `false` + enabled: + +# -- (bool) Sets the debug logs to this integration or all integrations if it is set globally. Can be configured also with `global.verboseLog` +# @default -- `false` +verboseLog: diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/.helmignore b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/.helmignore new file mode 100644 index 0000000000..1ed4e226ea --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/.helmignore @@ -0,0 +1,25 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ + +templates/apiservice/job-patch/README.md diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/Chart.lock b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/Chart.lock new file mode 100644 index 0000000000..23b2bd33c3 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: common-library + repository: https://helm-charts.newrelic.com + version: 1.3.0 +digest: sha256:2e1da613fd8a52706bde45af077779c5d69e9e1641bdf5c982eaf6d1ac67a443 +generated: "2024-08-30T23:31:11.079152974Z" diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/Chart.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/Chart.yaml new file mode 100644 index 0000000000..c7fc3f5fe1 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/Chart.yaml @@ -0,0 +1,25 @@ +apiVersion: v2 +appVersion: 0.13.2 +dependencies: +- name: common-library + repository: https://helm-charts.newrelic.com + version: 1.3.0 +description: A Helm chart to deploy the New Relic Kubernetes Metrics Adapter. +home: https://hub.docker.com/r/newrelic/newrelic-k8s-metrics-adapter +icon: https://newrelic.com/assets/newrelic/source/NewRelic-logo-square.svg +keywords: +- infrastructure +- newrelic +- monitoring +maintainers: +- name: juanjjaramillo + url: https://github.com/juanjjaramillo +- name: csongnr + url: https://github.com/csongnr +- name: dbudziwojskiNR + url: https://github.com/dbudziwojskiNR +name: newrelic-k8s-metrics-adapter +sources: +- https://github.com/newrelic/newrelic-k8s-metrics-adapter +- https://github.com/newrelic/newrelic-k8s-metrics-adapter/tree/main/charts/newrelic-k8s-metrics-adapter +version: 1.11.2 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/README.md b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/README.md new file mode 100644 index 0000000000..9c5428201c --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/README.md @@ -0,0 +1,139 @@ +[![New Relic Experimental header](https://github.com/newrelic/opensource-website/raw/master/src/images/categories/Experimental.png)](https://opensource.newrelic.com/oss-category/#new-relic-experimental) + +# newrelic-k8s-metrics-adapter + +A Helm chart to deploy the New Relic Kubernetes Metrics Adapter. + +**Homepage:** + +## Source Code + +* +* + +## Requirements + +| Repository | Name | Version | +|------------|------|---------| +| https://helm-charts.newrelic.com | common-library | 1.3.0 | + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| affinity | object | `{}` | Node affinity to use for scheduling. | +| apiServicePatchJob.image | object | See `values.yaml`. | Registry, repository, tag, and pull policy for the job container image. | +| apiServicePatchJob.volumeMounts | list | `[]` | Additional Volume mounts for Cert Job, you might want to mount tmp if Pod Security Policies. | +| apiServicePatchJob.volumes | list | `[]` | Additional Volumes for Cert Job. | +| certManager.enabled | bool | `false` | Use cert manager for APIService certs, rather than the built-in patch job. | +| config.accountID | string | `nil` | New Relic [Account ID](https://docs.newrelic.com/docs/accounts/accounts-billing/account-structure/account-id/) where the configured metrics are sourced from. (**Required**) | +| config.cacheTTLSeconds | int | `30` | Period of time in seconds in which a cached value of a metric is consider valid. | +| config.externalMetrics | string | See `values.yaml` | Contains all the external metrics definition of the adapter. Each key of the externalMetric entry represents the metric name and contains the parameters that defines it. | +| config.nrdbClientTimeoutSeconds | int | 30 | Defines the NRDB client timeout. The maximum allowed value is 120. | +| config.region | string | Automatically detected from `licenseKey`. | New Relic account region. If not set, it will be automatically derived from the License Key. | +| containerSecurityContext | string | `nil` | Configure containerSecurityContext | +| extraEnv | list | `[]` | Array to add extra environment variables | +| extraEnvFrom | list | `[]` | Array to add extra envFrom | +| extraVolumeMounts | list | `[]` | Add extra volume mounts | +| extraVolumes | list | `[]` | Array to add extra volumes | +| fullnameOverride | string | `""` | To fully override common.naming.fullname | +| image | object | See `values.yaml`. | Registry, repository, tag, and pull policy for the container image. | +| image.pullSecrets | list | `[]` | The image pull secrets. | +| nodeSelector | object | `{}` | Node label to use for scheduling. | +| personalAPIKey | string | `nil` | New Relic [Personal API Key](https://docs.newrelic.com/docs/apis/intro-apis/new-relic-api-keys/#user-api-key) (stored in a secret). Used to connect to NerdGraph in order to fetch the configured metrics. (**Required**) | +| podAnnotations | string | `nil` | Additional annotations to apply to the pod(s). | +| podSecurityContext | string | `nil` | Configure podSecurityContext | +| proxy | string | `nil` | Configure proxy for the metrics-adapter. | +| rbac.pspEnabled | bool | `false` | Whether the chart should create Pod Security Policy objects. | +| replicas | int | `1` | Number of replicas in the deployment. | +| resources | object | See `values.yaml` | Resources you wish to assign to the pod. | +| serviceAccount.create | string | `true` | Specifies whether a ServiceAccount should be created for the job and the deployment. false avoids creation, true or empty will create the ServiceAccount | +| serviceAccount.name | string | Automatically generated. | If `serviceAccount.create` this will be the name of the ServiceAccount to use. If not set and create is true, a name is generated using the fullname template. If create is false, a serviceAccount with the given name must exist | +| tolerations | list | `[]` | List of node taints to tolerate (requires Kubernetes >= 1.6) | +| verboseLog | bool | `false` | Enable metrics adapter verbose logs. | + +## Example + +Make sure you have [added the New Relic chart repository.](../../README.md#install) + +Because of metrics configuration, we recommend to use an external values file to deploy the chart. An example with the required parameters looks like: + +```yaml +cluster: ClusterName +personalAPIKey: +config: + accountID: + externalMetrics: + nginx_average_requests: + query: "FROM Metric SELECT average(nginx.server.net.requestsPerSecond) SINCE 2 MINUTES AGO" +``` + +Then, to install this chart, run the following command: + +```sh +helm upgrade --install [release-name] newrelic-k8s-metrics-adapter/newrelic-k8s-metrics-adapter --values [values file path] +``` + +Once deployed the metric `nginx_average_requests` will be available to use by any HPA. This is and example of an HPA yaml using this metric: + +```yaml +kind: HorizontalPodAutoscaler +apiVersion: autoscaling/v2beta2 +metadata: + name: nginx-scaler +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: nginx + minReplicas: 1 + maxReplicas: 10 + metrics: + - type: External + external: + metric: + name: nginx_average_requests + selector: + matchLabels: + k8s.namespaceName: nginx + target: + type: Value + value: 10000 +``` + +The NRQL query that will be run to get the `nginx_average_requests` value will be: + +```sql +FROM Metric SELECT average(nginx.server.net.requestsPerSecond) WHERE clusterName='ClusterName' AND `k8s.namespaceName`='nginx' SINCE 2 MINUTES AGO +``` + +## External Metrics + +An example of multiple external metrics defined: + +```yaml +externalMetrics: + nginx_average_requests: + query: "FROM Metric SELECT average(nginx.server.net.requestsPerSecond) SINCE 2 MINUTES AGO" + container_average_cores_utilization: + query: "FROM Metric SELECT average(`k8s.container.cpuCoresUtilization`) SINCE 2 MINUTES AGO" +``` + +## Resources + +The default set of resources assigned to the newrelic-k8s-metrics-adapter pods is shown below: + +```yaml +resources: + limits: + memory: 80M + requests: + cpu: 100m + memory: 30M +``` + +## Maintainers + +* [juanjjaramillo](https://github.com/juanjjaramillo) +* [csongnr](https://github.com/csongnr) +* [dbudziwojskiNR](https://github.com/dbudziwojskiNR) diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/README.md.gotmpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/README.md.gotmpl new file mode 100644 index 0000000000..1de8c95531 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/README.md.gotmpl @@ -0,0 +1,107 @@ +[![New Relic Experimental header](https://github.com/newrelic/opensource-website/raw/master/src/images/categories/Experimental.png)](https://opensource.newrelic.com/oss-category/#new-relic-experimental) + +{{ template "chart.header" . }} +{{ template "chart.deprecationWarning" . }} + +{{ template "chart.description" . }} + +{{ template "chart.homepageLine" . }} + +{{ template "chart.sourcesSection" . }} + +{{ template "chart.requirementsSection" . }} + +{{ template "chart.valuesSection" . }} + +## Example + +Make sure you have [added the New Relic chart repository.](../../README.md#install) + +Because of metrics configuration, we recommend to use an external values file to deploy the chart. An example with the required parameters looks like: + +```yaml +cluster: ClusterName +personalAPIKey: +config: + accountID: + externalMetrics: + nginx_average_requests: + query: "FROM Metric SELECT average(nginx.server.net.requestsPerSecond) SINCE 2 MINUTES AGO" +``` + +Then, to install this chart, run the following command: + +```sh +helm upgrade --install [release-name] newrelic-k8s-metrics-adapter/newrelic-k8s-metrics-adapter --values [values file path] +``` + +Once deployed the metric `nginx_average_requests` will be available to use by any HPA. This is and example of an HPA yaml using this metric: + +```yaml +kind: HorizontalPodAutoscaler +apiVersion: autoscaling/v2beta2 +metadata: + name: nginx-scaler +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: nginx + minReplicas: 1 + maxReplicas: 10 + metrics: + - type: External + external: + metric: + name: nginx_average_requests + selector: + matchLabels: + k8s.namespaceName: nginx + target: + type: Value + value: 10000 +``` + +The NRQL query that will be run to get the `nginx_average_requests` value will be: + +```sql +FROM Metric SELECT average(nginx.server.net.requestsPerSecond) WHERE clusterName='ClusterName' AND `k8s.namespaceName`='nginx' SINCE 2 MINUTES AGO +``` + +## External Metrics + +An example of multiple external metrics defined: + +```yaml +externalMetrics: + nginx_average_requests: + query: "FROM Metric SELECT average(nginx.server.net.requestsPerSecond) SINCE 2 MINUTES AGO" + container_average_cores_utilization: + query: "FROM Metric SELECT average(`k8s.container.cpuCoresUtilization`) SINCE 2 MINUTES AGO" +``` + +## Resources + +The default set of resources assigned to the newrelic-k8s-metrics-adapter pods is shown below: + +```yaml +resources: + limits: + memory: 80M + requests: + cpu: 100m + memory: 30M +``` + +{{ if .Maintainers }} +## Maintainers +{{ range .Maintainers }} +{{- if .Name }} +{{- if .Url }} +* [{{ .Name }}]({{ .Url }}) +{{- else }} +* {{ .Name }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/.helmignore b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/.helmignore new file mode 100644 index 0000000000..0e8a0eb36f --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/.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 +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/Chart.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/Chart.yaml new file mode 100644 index 0000000000..f2ee5497ee --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/Chart.yaml @@ -0,0 +1,17 @@ +apiVersion: v2 +description: Provides helpers to provide consistency on all the charts +keywords: +- newrelic +- chart-library +maintainers: +- name: juanjjaramillo + url: https://github.com/juanjjaramillo +- name: csongnr + url: https://github.com/csongnr +- name: dbudziwojskiNR + url: https://github.com/dbudziwojskiNR +- name: kang-makes + url: https://github.com/kang-makes +name: common-library +type: library +version: 1.3.0 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/DEVELOPERS.md b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/DEVELOPERS.md new file mode 100644 index 0000000000..7208c673ec --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/DEVELOPERS.md @@ -0,0 +1,747 @@ +# Functions/templates documented for chart writers +Here is some rough documentation separated by the file that contains the function, the function +name and how to use it. We are not covering functions that start with `_` (e.g. +`newrelic.common.license._licenseKey`) because they are used internally by this library for +other helpers. Helm does not have the concept of "public" or "private" functions/templates so +this is a convention of ours. + +## _naming.tpl +These functions are used to name objects. + +### `newrelic.common.naming.name` +This is the same as the idiomatic `CHART-NAME.name` that is created when you use `helm create`. + +It honors `.Values.nameOverride`. + +Usage: +```mustache +{{ include "newrelic.common.naming.name" . }} +``` + +### `newrelic.common.naming.fullname` +This is the same as the idiomatic `CHART-NAME.fullname` that is created when you use `helm create` + +It honors `.Values.fullnameOverride`. + +Usage: +```mustache +{{ include "newrelic.common.naming.fullname" . }} +``` + +### `newrelic.common.naming.chart` +This is the same as the idiomatic `CHART-NAME.chart` that is created when you use `helm create`. + +It is mostly useless for chart writers. It is used internally for templating the labels but there +is no reason to keep it "private". + +Usage: +```mustache +{{ include "newrelic.common.naming.chart" . }} +``` + +### `newrelic.common.naming.truncateToDNS` +This is a useful template that could be used to trim a string to 63 chars and does not end with a dash (`-`). +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). + +Usage: +```mustache +{{ $nameToTruncate := "a-really-really-really-really-REALLY-long-string-that-should-be-truncated-because-it-is-enought-long-to-brak-something" +{{- $truncatedName := include "newrelic.common.naming.truncateToDNS" $nameToTruncate }} +{{- $truncatedName }} +{{- /* This should print: a-really-really-really-really-REALLY-long-string-that-should-be */ -}} +``` + +### `newrelic.common.naming.truncateToDNSWithSuffix` +This template function is the same as the above but instead of receiving a string you should give a `dict` +with a `name` and a `suffix`. This function will join them with a dash (`-`) and trim the `name` so the +result of `name-suffix` is no more than 63 chars + +Usage: +```mustache +{{ $nameToTruncate := "a-really-really-really-really-REALLY-long-string-that-should-be-truncated-because-it-is-enought-long-to-brak-something" +{{- $suffix := "A-NOT-SO-LONG-SUFFIX" }} +{{- $truncatedName := include "truncateToDNSWithSuffix" (dict "name" $nameToTruncate "suffix" $suffix) }} +{{- $truncatedName }} +{{- /* This should print: a-really-really-really-really-REALLY-long-A-NOT-SO-LONG-SUFFIX */ -}} +``` + + + +## _labels.tpl +### `newrelic.common.labels`, `newrelic.common.labels.selectorLabels` and `newrelic.common.labels.podLabels` +These are functions that are used to label objects. They are configured by this `values.yaml` +```yaml +global: + podLabels: {} # included in all the pods of all the charts that implement this library + labels: {} # included in all the objects of all the charts that implement this library +podLabels: {} # included in all the pods of this chart +labels: {} # included in all the objects of this chart +``` + +label maps are merged from global to local values. + +And chart writer should use them like this: +```mustache +metadata: + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +spec: + selector: + matchLabels: + {{- include "newrelic.common.labels.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "newrelic.common.labels.podLabels" . | nindent 8 }} +``` + +`newrelic.common.labels.podLabels` includes `newrelic.common.labels.selectorLabels` automatically. + + + +## _priority-class-name.tpl +### `newrelic.common.priorityClassName` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + priorityClassName: "" +priorityClassName: "" +``` + +Be careful: chart writers should put an empty string (or any kind of Helm falsiness) for this +library to work properly. If in your values a non-falsy `priorityClassName` is found, the global +one is going to be always ignored. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.priorityClassName" . }} + priorityClassName: {{ . }} + {{- end }} +``` + + + +## _hostnetwork.tpl +### `newrelic.common.hostNetwork` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + hostNetwork: # Note that this is empty (nil) +hostNetwork: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `hostNetwork` is defined, the global one is going to be always ignored. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.hostNetwork" . }} + hostNetwork: {{ . }} + {{- end }} +``` + +### `newrelic.common.hostNetwork.value` +This function is an abstraction of the function above but this returns directly "true" or "false". + +Be careful with using this with an `if` as Helm does evaluate "false" (string) as `true`. + +Usage (example in a pod spec): +```mustache +spec: + hostNetwork: {{ include "newrelic.common.hostNetwork.value" . }} +``` + + + +## _dnsconfig.tpl +### `newrelic.common.dnsConfig` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + dnsConfig: {} +dnsConfig: {} +``` + +Be careful: chart writers should put an empty string (or any kind of Helm falsiness) for this +library to work properly. If in your values a non-falsy `dnsConfig` is found, the global +one is going to be always ignored. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.dnsConfig" . }} + dnsConfig: + {{- . | nindent 4 }} + {{- end }} +``` + + + +## _images.tpl +These functions help us to deal with how images are templated. This allows setting `registries` +where to fetch images globally while being flexible enough to fit in different maps of images +and deployments with one or more images. This is the example of a complex `values.yaml` that +we are going to use during the documentation of these functions: + +```yaml +global: + images: + registry: nexus-3-instance.internal.clients-domain.tld +jobImage: + registry: # defaults to "example.tld" when empty in these examples + repository: ingress-nginx/kube-webhook-certgen + tag: v1.1.1 + pullPolicy: IfNotPresent + pullSecrets: [] +images: + integration: + registry: + repository: newrelic/nri-kube-events + tag: 1.8.0 + pullPolicy: IfNotPresent + agent: + registry: + repository: newrelic/k8s-events-forwarder + tag: 1.22.0 + pullPolicy: IfNotPresent + pullSecrets: [] +``` + +### `newrelic.common.images.image` +This will return a string with the image ready to be downloaded that includes the registry, the image and the tag. +`defaultRegistry` is used to keep `registry` field empty in `values.yaml` so you can override the image using +`global.images.registry`, your local `jobImage.registry` and be able to fallback to a registry that is not `docker.io` +(Or the default repository that the client could have set in the CRI). + +Usage: +```mustache +{{- /* For the integration */}} +{{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.images.agent "context" .) }} +{{- /* For jobImage */}} +{{ include "newrelic.common.images.image" ( dict "defaultRegistry" "example.tld" "imageRoot" .Values.jobImage "context" .) }} +``` + +### `newrelic.common.images.registry` +It returns the registry from the global or local values. You should avoid using this helper to create your image +URL and use `newrelic.common.images.image` instead, but it is there to be used in case it is needed. + +Usage: +```mustache +{{- /* For the integration */}} +{{ include "newrelic.common.images.registry" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.registry" ( dict "imageRoot" .Values.images.agent "context" .) }} +{{- /* For jobImage */}} +{{ include "newrelic.common.images.registry" ( dict "defaultRegistry" "example.tld" "imageRoot" .Values.jobImage "context" .) }} +``` + +### `newrelic.common.images.repository` +It returns the image from the values. You should avoid using this helper to create your image +URL and use `newrelic.common.images.image` instead, but it is there to be used in case it is needed. + +Usage: +```mustache +{{- /* For jobImage */}} +{{ include "newrelic.common.images.repository" ( dict "imageRoot" .Values.jobImage "context" .) }} +{{- /* For the integration */}} +{{ include "newrelic.common.images.repository" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.repository" ( dict "imageRoot" .Values.images.agent "context" .) }} +``` + +### `newrelic.common.images.tag` +It returns the image's tag from the values. You should avoid using this helper to create your image +URL and use `newrelic.common.images.image` instead, but it is there to be used in case it is needed. + +Usage: +```mustache +{{- /* For jobImage */}} +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.jobImage "context" .) }} +{{- /* For the integration */}} +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.images.agent "context" .) }} +``` + +### `newrelic.common.images.renderPullSecrets` +If returns a merged map that contains the pull secrets from the global configuration and the local one. + +Usage: +```mustache +{{- /* For jobImage */}} +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" .Values.jobImage.pullSecrets "context" .) }} +{{- /* For the integration */}} +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" .Values.images.pullSecrets "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" .Values.images.pullSecrets "context" .) }} +``` + + + +## _serviceaccount.tpl +These functions are used to evaluate if the service account should be created, with which name and add annotations to it. + +The functions that the common library has implemented for service accounts are: +* `newrelic.common.serviceAccount.create` +* `newrelic.common.serviceAccount.name` +* `newrelic.common.serviceAccount.annotations` + +Usage: +```mustache +{{- if include "newrelic.common.serviceAccount.create" . -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + {{- with (include "newrelic.common.serviceAccount.annotations" .) }} + annotations: + {{- . | nindent 4 }} + {{- end }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "newrelic.common.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} +{{- end }} +``` + + + +## _affinity.tpl, _nodeselector.tpl and _tolerations.tpl +These three files are almost the same and they follow the idiomatic way of `helm create`. + +Each function also looks if there is a global value like the other helpers. +```yaml +global: + affinity: {} + nodeSelector: {} + tolerations: [] +affinity: {} +nodeSelector: {} +tolerations: [] +``` + +The values here are replaced instead of be merged. If a value at root level is found, the global one is ignored. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.nodeSelector" . }} + nodeSelector: + {{- . | nindent 4 }} + {{- end }} + {{- with include "newrelic.common.affinity" . }} + affinity: + {{- . | nindent 4 }} + {{- end }} + {{- with include "newrelic.common.tolerations" . }} + tolerations: + {{- . | nindent 4 }} + {{- end }} +``` + + + +## _agent-config.tpl +### `newrelic.common.agentConfig.defaults` +This returns a YAML that the agent can use directly as a config that includes other options from the values file like verbose mode, +custom attributes, FedRAMP and such. + +Usage: +```mustache +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include newrelic.common.naming.truncateToDNSWithSuffix (dict "name" (include "newrelic.common.naming.fullname" .) suffix "agent-config") }} + namespace: {{ .Release.Namespace }} +data: + newrelic-infra.yml: |- + # This is the configuration file for the infrastructure agent. See: + # https://docs.newrelic.com/docs/infrastructure/install-infrastructure-agent/configuration/infrastructure-agent-configuration-settings/ + {{- include "newrelic.common.agentConfig.defaults" . | nindent 4 }} +``` + + + +## _cluster.tpl +### `newrelic.common.cluster` +Returns the cluster name + +Usage: +```mustache +{{ include "newrelic.common.cluster" . }} +``` + + + +## _custom-attributes.tpl +### `newrelic.common.customAttributes` +Return custom attributes in YAML format. + +Usage: +```mustache +apiVersion: v1 +kind: ConfigMap +metadata: + name: example +data: + custom-attributes.yaml: | + {{- include "newrelic.common.customAttributes" . | nindent 4 }} + custom-attributes.json: | + {{- include "newrelic.common.customAttributes" . | fromYaml | toJson | nindent 4 }} +``` + + + +## _fedramp.tpl +### `newrelic.common.fedramp.enabled` +Returns true if FedRAMP is enabled or an empty string if not. It can be safely used in conditionals as an empty string is a Helm falsiness. + +Usage: +```mustache +{{ include "newrelic.common.fedramp.enabled" . }} +``` + +### `newrelic.common.fedramp.enabled.value` +Returns true if FedRAMP is enabled or false if not. This is to have the value of FedRAMP ready to be templated. + +Usage: +```mustache +{{ include "newrelic.common.fedramp.enabled.value" . }} +``` + + + +## _license.tpl +### `newrelic.common.license.secretName` and ### `newrelic.common.license.secretKeyName` +Returns the secret and key inside the secret where to read the license key. + +The common library will take care of using a user-provided custom secret or creating a secret that contains the license key. + +To create the secret use `newrelic.common.license.secret`. + +Usage: +```mustache +{{- if and (.Values.controlPlane.enabled) (not (include "newrelic.fargate" .)) }} +apiVersion: v1 +kind: Pod +metadata: + name: example +spec: + containers: + - name: agent + env: + - name: "NRIA_LICENSE_KEY" + valueFrom: + secretKeyRef: + name: {{ include "newrelic.common.license.secretName" . }} + key: {{ include "newrelic.common.license.secretKeyName" . }} +``` + + + +## _license_secret.tpl +### `newrelic.common.license.secret` +This function templates the secret that is used by agents and integrations with the license Key provided by the user. It will +template nothing (empty string) if the user provides a custom pair of secret name and key. + +This template also fails in case the user has not provided any license key or custom secret so no safety checks have to be done +by chart writers. + +You just must have a template with these two lines: +```mustache +{{- /* Common library will take care of creating the secret or not. */ -}} +{{- include "newrelic.common.license.secret" . -}} +``` + + + +## _insights.tpl +### `newrelic.common.insightsKey.secretName` and ### `newrelic.common.insightsKey.secretKeyName` +Returns the secret and key inside the secret where to read the insights key. + +The common library will take care of using a user-provided custom secret or creating a secret that contains the insights key. + +To create the secret use `newrelic.common.insightsKey.secret`. + +Usage: +```mustache +apiVersion: v1 +kind: Pod +metadata: + name: statsd +spec: + containers: + - name: statsd + env: + - name: "INSIGHTS_KEY" + valueFrom: + secretKeyRef: + name: {{ include "newrelic.common.insightsKey.secretName" . }} + key: {{ include "newrelic.common.insightsKey.secretKeyName" . }} +``` + + + +## _insights_secret.tpl +### `newrelic.common.insightsKey.secret` +This function templates the secret that is used by agents and integrations with the insights key provided by the user. It will +template nothing (empty string) if the user provides a custom pair of secret name and key. + +This template also fails in case the user has not provided any insights key or custom secret so no safety checks have to be done +by chart writers. + +You just must have a template with these two lines: +```mustache +{{- /* Common library will take care of creating the secret or not. */ -}} +{{- include "newrelic.common.insightsKey.secret" . -}} +``` + + + +## _userkey.tpl +### `newrelic.common.userKey.secretName` and ### `newrelic.common.userKey.secretKeyName` +Returns the secret and key inside the secret where to read a user key. + +The common library will take care of using a user-provided custom secret or creating a secret that contains the insights key. + +To create the secret use `newrelic.common.userKey.secret`. + +Usage: +```mustache +apiVersion: v1 +kind: Pod +metadata: + name: statsd +spec: + containers: + - name: statsd + env: + - name: "API_KEY" + valueFrom: + secretKeyRef: + name: {{ include "newrelic.common.userKey.secretName" . }} + key: {{ include "newrelic.common.userKey.secretKeyName" . }} +``` + + + +## _userkey_secret.tpl +### `newrelic.common.userKey.secret` +This function templates the secret that is used by agents and integrations with a user key provided by the user. It will +template nothing (empty string) if the user provides a custom pair of secret name and key. + +This template also fails in case the user has not provided any API key or custom secret so no safety checks have to be done +by chart writers. + +You just must have a template with these two lines: +```mustache +{{- /* Common library will take care of creating the secret or not. */ -}} +{{- include "newrelic.common.userKey.secret" . -}} +``` + + + +## _region.tpl +### `newrelic.common.region.validate` +Given a string, return a normalized name for the region if valid. + +This function does not need the context of the chart, only the value to be validated. The region returned +honors the region [definition of the newrelic-client-go implementation](https://github.com/newrelic/newrelic-client-go/blob/cbe3e4cf2b95fd37095bf2ffdc5d61cffaec17e2/pkg/region/region_constants.go#L8-L21) +so (as of 2024/09/14) it returns the region as "US", "EU", "Staging", or "Local". + +In case the region provided does not match these 4, the helper calls `fail` and abort the templating. + +Usage: +```mustache +{{ include "newrelic.common.region.validate" "us" }} +``` + +### `newrelic.common.region` +It reads global and local variables for `region`: +```yaml +global: + region: # Note that this can be empty (nil) or "" (empty string) +region: # Note that this can be empty (nil) or "" (empty string) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in your +values a `region` is defined, the global one is going to be always ignored. + +This function gives protection so it enforces users to give the license key as a value in their +`values.yaml` or specify a global or local `region` value. To understand how the `region` value +works, read the documentation of `newrelic.common.region.validate`. + +The function will change the region from US, EU or Staging based of the license key and the +`nrStaging` toggle. Whichever region is computed from the license/toggle can be overridden by +the `region` value. + +Usage: +```mustache +{{ include "newrelic.common.region" . }} +``` + + + +## _low-data-mode.tpl +### `newrelic.common.lowDataMode` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + lowDataMode: # Note that this is empty (nil) +lowDataMode: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `lowdataMode` is defined, the global one is going to be always ignored. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage: +```mustache +{{ include "newrelic.common.lowDataMode" . }} +``` + + + +## _privileged.tpl +### `newrelic.common.privileged` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + privileged: # Note that this is empty (nil) +privileged: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `privileged` is defined, the global one is going to be always ignored. + +Chart writers could override this and put directly a `true` in the `values.yaml` to override the +default of the common library. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage: +```mustache +{{ include "newrelic.common.privileged" . }} +``` + +### `newrelic.common.privileged.value` +Returns true if privileged mode is enabled or false if not. This is to have the value of privileged ready to be templated. + +Usage: +```mustache +{{ include "newrelic.common.privileged.value" . }} +``` + + + +## _proxy.tpl +### `newrelic.common.proxy` +Returns the proxy URL configured by the user. + +Usage: +```mustache +{{ include "newrelic.common.proxy" . }} +``` + + + +## _security-context.tpl +Use these functions to share the security context among all charts. Useful in clusters that have security enforcing not to +use the root user (like OpenShift) or users that have an admission webhooks. + +The functions are: +* `newrelic.common.securityContext.container` +* `newrelic.common.securityContext.pod` + +Usage: +```mustache +apiVersion: v1 +kind: Pod +metadata: + name: example +spec: + spec: + {{- with include "newrelic.common.securityContext.pod" . }} + securityContext: + {{- . | nindent 8 }} + {{- end }} + + containers: + - name: example + {{- with include "nriKubernetes.securityContext.container" . }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} +``` + + + +## _staging.tpl +### `newrelic.common.nrStaging` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + nrStaging: # Note that this is empty (nil) +nrStaging: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `nrStaging` is defined, the global one is going to be always ignored. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage: +```mustache +{{ include "newrelic.common.nrStaging" . }} +``` + +### `newrelic.common.nrStaging.value` +Returns true if staging is enabled or false if not. This is to have the staging value ready to be templated. + +Usage: +```mustache +{{ include "newrelic.common.nrStaging.value" . }} +``` + + + +## _verbose-log.tpl +### `newrelic.common.verboseLog` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + verboseLog: # Note that this is empty (nil) +verboseLog: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `verboseLog` is defined, the global one is going to be always ignored. + +Usage: +```mustache +{{ include "newrelic.common.verboseLog" . }} +``` + +### `newrelic.common.verboseLog.valueAsBoolean` +Returns true if verbose is enabled or false if not. This is to have the verbose value ready to be templated as a boolean + +Usage: +```mustache +{{ include "newrelic.common.verboseLog.valueAsBoolean" . }} +``` + +### `newrelic.common.verboseLog.valueAsInt` +Returns 1 if verbose is enabled or 0 if not. This is to have the verbose value ready to be templated as an integer + +Usage: +```mustache +{{ include "newrelic.common.verboseLog.valueAsInt" . }} +``` diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/README.md b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/README.md new file mode 100644 index 0000000000..10f08ca677 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/README.md @@ -0,0 +1,106 @@ +# Helm Common library + +The common library is a way to unify the UX through all the Helm charts that implement it. + +The tooling suite that New Relic is huge and growing and this allows to set things globally +and locally for a single chart. + +## Documentation for chart writers + +If you are writing a chart that is going to use this library you can check the [developers guide](/library/common-library/DEVELOPERS.md) to see all +the functions/templates that we have implemented, what they do and how to use them. + +## Values managed globally + +We want to have a seamless experience through all the charts so we created this library that tries to standardize the behaviour +of all the charts. Sadly, because of the complexity of all these integrations, not all the charts behave exactly as expected. + +An example is `newrelic-infrastructure` that ignores `hostNetwork` in the control plane scraper because most of the users has the +control plane listening in the node to `localhost`. + +For each chart that has a special behavior (or further information of the behavior) there is a "chart particularities" section +in its README.md that explains which is the expected behavior. + +At the time of writing this, all the charts from `nri-bundle` except `newrelic-logging` and `synthetics-minion` implements this +library and honors global options as described in this document. + +Here is a list of global options: + +| Global keys | Local keys | Default | Merged[1](#values-managed-globally-1) | Description | +|-------------|------------|---------|--------------------------------------------------|-------------| +| global.cluster | cluster | `""` | | Name of the Kubernetes cluster monitored | +| global.licenseKey | licenseKey | `""` | | This set this license key to use | +| global.customSecretName | customSecretName | `""` | | In case you don't want to have the license key in you values, this allows you to point to a user created secret to get the key from there | +| global.customSecretLicenseKey | customSecretLicenseKey | `""` | | In case you don't want to have the license key in you values, this allows you to point to which secret key is the license key located | +| global.podLabels | podLabels | `{}` | yes | Additional labels for chart pods | +| global.labels | labels | `{}` | yes | Additional labels for chart objects | +| global.priorityClassName | priorityClassName | `""` | | Sets pod's priorityClassName | +| global.hostNetwork | hostNetwork | `false` | | Sets pod's hostNetwork | +| global.dnsConfig | dnsConfig | `{}` | | Sets pod's dnsConfig | +| global.images.registry | See [Further information](#values-managed-globally-2) | `""` | | Changes the registry where to get the images. Useful when there is an internal image cache/proxy | +| global.images.pullSecrets | See [Further information](#values-managed-globally-2) | `[]` | yes | Set secrets to be able to fetch images | +| global.podSecurityContext | podSecurityContext | `{}` | | Sets security context (at pod level) | +| global.containerSecurityContext | containerSecurityContext | `{}` | | Sets security context (at container level) | +| global.affinity | affinity | `{}` | | Sets pod/node affinities | +| global.nodeSelector | nodeSelector | `{}` | | Sets pod's node selector | +| global.tolerations | tolerations | `[]` | | Sets pod's tolerations to node taints | +| global.serviceAccount.create | serviceAccount.create | `true` | | Configures if the service account should be created or not | +| global.serviceAccount.name | serviceAccount.name | name of the release | | Change the name of the service account. This is honored if you disable on this cahrt the creation of the service account so you can use your own. | +| global.serviceAccount.annotations | serviceAccount.annotations | `{}` | yes | Add these annotations to the service account we create | +| global.customAttributes | customAttributes | `{}` | | Adds extra attributes to the cluster and all the metrics emitted to the backend | +| global.fedramp | fedramp | `false` | | Enables FedRAMP | +| global.lowDataMode | lowDataMode | `false` | | Reduces number of metrics sent in order to reduce costs | +| global.privileged | privileged | Depends on the chart | | In each integration it has different behavior. See [Further information](#values-managed-globally-3) but all aims to send less metrics to the backend to try to save costs | +| global.proxy | proxy | `""` | | Configures the integration to send all HTTP/HTTPS request through the proxy in that URL. The URL should have a standard format like `https://user:password@hostname:port` | +| global.nrStaging | nrStaging | `false` | | Send the metrics to the staging backend. Requires a valid staging license key | +| global.verboseLog | verboseLog | `false` | | Sets the debug/trace logs to this integration or all integrations if it is set globally | + +### Further information + +#### 1. Merged + +Merged means that the values from global are not replaced by the local ones. Think in this example: +```yaml +global: + labels: + global: global + hostNetwork: true + nodeSelector: + global: global + +labels: + local: local +nodeSelector: + local: local +hostNetwork: false +``` + +This values will template `hostNetwork` to `false`, a map of labels `{ "global": "global", "local": "local" }` and a `nodeSelector` with +`{ "local": "local" }`. + +As Helm by default merges all the maps it could be confusing that we have two behaviors (merging `labels` and replacing `nodeSelector`) +the `values` from global to local. This is the rationale behind this: +* `hostNetwork` is templated to `false` because is overriding the value defined globally. +* `labels` are merged because the user may want to label all the New Relic pods at once and label other solution pods differently for + clarity' sake. +* `nodeSelector` does not merge as `labels` because could make it harder to overwrite/delete a selector that comes from global because + of the logic that Helm follows merging maps. + + +#### 2. Fine grain registries + +Some charts only have 1 image while others that can have 2 or more images. The local path for the registry can change depending +on the chart itself. + +As this is mostly unique per helm chart, you should take a look to the chart's values table (or directly to the `values.yaml` file to see all the +images that you can change. + +This should only be needed if you have an advanced setup that forces you to have granularity enough to force a proxy/cache registry per integration. + + + +#### 3. Privileged mode + +By default, from the common library, the privileged mode is set to false. But most of the helm charts require this to be true to fetch more +metrics so could see a true in some charts. The consequences of the privileged mode differ from one chart to another so for each chart that +honors the privileged mode toggle should be a section in the README explaining which is the behavior with it enabled or disabled. diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_affinity.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_affinity.tpl new file mode 100644 index 0000000000..1b2636754e --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_affinity.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod affinity */ -}} +{{- define "newrelic.common.affinity" -}} + {{- if .Values.affinity -}} + {{- toYaml .Values.affinity -}} + {{- else if .Values.global -}} + {{- if .Values.global.affinity -}} + {{- toYaml .Values.global.affinity -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_agent-config.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_agent-config.tpl new file mode 100644 index 0000000000..9c32861a02 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_agent-config.tpl @@ -0,0 +1,26 @@ +{{/* +This helper should return the defaults that all agents should have +*/}} +{{- define "newrelic.common.agentConfig.defaults" -}} +{{- if include "newrelic.common.verboseLog" . }} +log: + level: trace +{{- end }} + +{{- if (include "newrelic.common.nrStaging" . ) }} +staging: true +{{- end }} + +{{- with include "newrelic.common.proxy" . }} +proxy: {{ . | quote }} +{{- end }} + +{{- with include "newrelic.common.fedramp.enabled" . }} +fedramp: {{ . }} +{{- end }} + +{{- with fromYaml ( include "newrelic.common.customAttributes" . ) }} +custom_attributes: + {{- toYaml . | nindent 2 }} +{{- end }} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_cluster.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_cluster.tpl new file mode 100644 index 0000000000..0197dd35a3 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_cluster.tpl @@ -0,0 +1,15 @@ +{{/* +Return the cluster +*/}} +{{- define "newrelic.common.cluster" -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} + +{{- if .Values.cluster -}} + {{- .Values.cluster -}} +{{- else if $global.cluster -}} + {{- $global.cluster -}} +{{- else -}} + {{ fail "There is not cluster name definition set neither in `.global.cluster' nor `.cluster' in your values.yaml. Cluster name is required." }} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_custom-attributes.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_custom-attributes.tpl new file mode 100644 index 0000000000..92020719c3 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_custom-attributes.tpl @@ -0,0 +1,17 @@ +{{/* +This will render custom attributes as a YAML ready to be templated or be used with `fromYaml`. +*/}} +{{- define "newrelic.common.customAttributes" -}} +{{- $customAttributes := dict -}} + +{{- $global := index .Values "global" | default dict -}} +{{- if $global.customAttributes -}} +{{- $customAttributes = mergeOverwrite $customAttributes $global.customAttributes -}} +{{- end -}} + +{{- if .Values.customAttributes -}} +{{- $customAttributes = mergeOverwrite $customAttributes .Values.customAttributes -}} +{{- end -}} + +{{- toYaml $customAttributes -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_dnsconfig.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_dnsconfig.tpl new file mode 100644 index 0000000000..d4e40aa8af --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_dnsconfig.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod dnsConfig */ -}} +{{- define "newrelic.common.dnsConfig" -}} + {{- if .Values.dnsConfig -}} + {{- toYaml .Values.dnsConfig -}} + {{- else if .Values.global -}} + {{- if .Values.global.dnsConfig -}} + {{- toYaml .Values.global.dnsConfig -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_fedramp.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_fedramp.tpl new file mode 100644 index 0000000000..9df8d6b5e9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_fedramp.tpl @@ -0,0 +1,25 @@ +{{- /* Defines the fedRAMP flag */ -}} +{{- define "newrelic.common.fedramp.enabled" -}} + {{- if .Values.fedramp -}} + {{- if .Values.fedramp.enabled -}} + {{- .Values.fedramp.enabled -}} + {{- end -}} + {{- else if .Values.global -}} + {{- if .Values.global.fedramp -}} + {{- if .Values.global.fedramp.enabled -}} + {{- .Values.global.fedramp.enabled -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} + + + +{{- /* Return FedRAMP value directly ready to be templated */ -}} +{{- define "newrelic.common.fedramp.enabled.value" -}} +{{- if include "newrelic.common.fedramp.enabled" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_hostnetwork.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_hostnetwork.tpl new file mode 100644 index 0000000000..4cf017ef7e --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_hostnetwork.tpl @@ -0,0 +1,39 @@ +{{- /* +Abstraction of the hostNetwork toggle. +This helper allows to override the global `.global.hostNetwork` with the value of `.hostNetwork`. +Returns "true" if `hostNetwork` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.hostNetwork" -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} + +{{- /* +`get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs + +We also want only to return when this is true, returning `false` here will template "false" (string) when doing +an `(include "newrelic.common.hostNetwork" .)`, which is not an "empty string" so it is `true` if it is used +as an evaluation somewhere else. +*/ -}} +{{- if get .Values "hostNetwork" | kindIs "bool" -}} + {{- if .Values.hostNetwork -}} + {{- .Values.hostNetwork -}} + {{- end -}} +{{- else if get $global "hostNetwork" | kindIs "bool" -}} + {{- if $global.hostNetwork -}} + {{- $global.hostNetwork -}} + {{- end -}} +{{- end -}} +{{- end -}} + + +{{- /* +Abstraction of the hostNetwork toggle. +This helper abstracts the function "newrelic.common.hostNetwork" to return true or false directly. +*/ -}} +{{- define "newrelic.common.hostNetwork.value" -}} +{{- if include "newrelic.common.hostNetwork" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_images.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_images.tpl new file mode 100644 index 0000000000..d4fb432905 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_images.tpl @@ -0,0 +1,94 @@ +{{- /* +Return the proper image name +{{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.path.to.the.image "defaultRegistry" "your.private.registry.tld" "context" .) }} +*/ -}} +{{- define "newrelic.common.images.image" -}} + {{- $registryName := include "newrelic.common.images.registry" ( dict "imageRoot" .imageRoot "defaultRegistry" .defaultRegistry "context" .context ) -}} + {{- $repositoryName := include "newrelic.common.images.repository" .imageRoot -}} + {{- $tag := include "newrelic.common.images.tag" ( dict "imageRoot" .imageRoot "context" .context) -}} + + {{- if $registryName -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag | quote -}} + {{- else -}} + {{- printf "%s:%s" $repositoryName $tag | quote -}} + {{- end -}} +{{- end -}} + + + +{{- /* +Return the proper image registry +{{ include "newrelic.common.images.registry" ( dict "imageRoot" .Values.path.to.the.image "defaultRegistry" "your.private.registry.tld" "context" .) }} +*/ -}} +{{- define "newrelic.common.images.registry" -}} +{{- $globalRegistry := "" -}} +{{- if .context.Values.global -}} + {{- if .context.Values.global.images -}} + {{- with .context.Values.global.images.registry -}} + {{- $globalRegistry = . -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- $localRegistry := "" -}} +{{- if .imageRoot.registry -}} + {{- $localRegistry = .imageRoot.registry -}} +{{- end -}} + +{{- $registry := $localRegistry | default $globalRegistry | default .defaultRegistry -}} +{{- if $registry -}} + {{- $registry -}} +{{- end -}} +{{- end -}} + + + +{{- /* +Return the proper image repository +{{ include "newrelic.common.images.repository" .Values.path.to.the.image }} +*/ -}} +{{- define "newrelic.common.images.repository" -}} + {{- .repository -}} +{{- end -}} + + + +{{- /* +Return the proper image tag +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.path.to.the.image "context" .) }} +*/ -}} +{{- define "newrelic.common.images.tag" -}} + {{- .imageRoot.tag | default .context.Chart.AppVersion | toString -}} +{{- end -}} + + + +{{- /* +Return the proper Image Pull Registry Secret Names evaluating values as templates +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" (list .Values.path.to.the.images.pullSecrets1, .Values.path.to.the.images.pullSecrets2) "context" .) }} +*/ -}} +{{- define "newrelic.common.images.renderPullSecrets" -}} + {{- $flatlist := list }} + + {{- if .context.Values.global -}} + {{- if .context.Values.global.images -}} + {{- if .context.Values.global.images.pullSecrets -}} + {{- range .context.Values.global.images.pullSecrets -}} + {{- $flatlist = append $flatlist . -}} + {{- end -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- range .pullSecrets -}} + {{- if not (empty .) -}} + {{- range . -}} + {{- $flatlist = append $flatlist . -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- if $flatlist -}} + {{- toYaml $flatlist -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_insights.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_insights.tpl new file mode 100644 index 0000000000..895c377326 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_insights.tpl @@ -0,0 +1,56 @@ +{{/* +Return the name of the secret holding the Insights Key. +*/}} +{{- define "newrelic.common.insightsKey.secretName" -}} +{{- $default := include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "insightskey" ) -}} +{{- include "newrelic.common.insightsKey._customSecretName" . | default $default -}} +{{- end -}} + +{{/* +Return the name key for the Insights Key inside the secret. +*/}} +{{- define "newrelic.common.insightsKey.secretKeyName" -}} +{{- include "newrelic.common.insightsKey._customSecretKey" . | default "insightsKey" -}} +{{- end -}} + +{{/* +Return local insightsKey if set, global otherwise. +This helper is for internal use. +*/}} +{{- define "newrelic.common.insightsKey._licenseKey" -}} +{{- if .Values.insightsKey -}} + {{- .Values.insightsKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.insightsKey -}} + {{- .Values.global.insightsKey -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name of the secret holding the Insights Key. +This helper is for internal use. +*/}} +{{- define "newrelic.common.insightsKey._customSecretName" -}} +{{- if .Values.customInsightsKeySecretName -}} + {{- .Values.customInsightsKeySecretName -}} +{{- else if .Values.global -}} + {{- if .Values.global.customInsightsKeySecretName -}} + {{- .Values.global.customInsightsKeySecretName -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name key for the Insights Key inside the secret. +This helper is for internal use. +*/}} +{{- define "newrelic.common.insightsKey._customSecretKey" -}} +{{- if .Values.customInsightsKeySecretKey -}} + {{- .Values.customInsightsKeySecretKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.customInsightsKeySecretKey }} + {{- .Values.global.customInsightsKeySecretKey -}} + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_insights_secret.yaml.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_insights_secret.yaml.tpl new file mode 100644 index 0000000000..556caa6ca6 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_insights_secret.yaml.tpl @@ -0,0 +1,21 @@ +{{/* +Renders the insights key secret if user has not specified a custom secret. +*/}} +{{- define "newrelic.common.insightsKey.secret" }} +{{- if not (include "newrelic.common.insightsKey._customSecretName" .) }} +{{- /* Fail if licenseKey is empty and required: */ -}} +{{- if not (include "newrelic.common.insightsKey._licenseKey" .) }} + {{- fail "You must specify a insightsKey or a customInsightsSecretName containing it" }} +{{- end }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "newrelic.common.insightsKey.secretName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +data: + {{ include "newrelic.common.insightsKey.secretKeyName" . }}: {{ include "newrelic.common.insightsKey._licenseKey" . | b64enc }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_labels.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_labels.tpl new file mode 100644 index 0000000000..b025948285 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_labels.tpl @@ -0,0 +1,54 @@ +{{/* +This will render the labels that should be used in all the manifests used by the helm chart. +*/}} +{{- define "newrelic.common.labels" -}} +{{- $global := index .Values "global" | default dict -}} + +{{- $chart := dict "helm.sh/chart" (include "newrelic.common.naming.chart" . ) -}} +{{- $managedBy := dict "app.kubernetes.io/managed-by" .Release.Service -}} +{{- $selectorLabels := fromYaml (include "newrelic.common.labels.selectorLabels" . ) -}} + +{{- $labels := mustMergeOverwrite $chart $managedBy $selectorLabels -}} +{{- if .Chart.AppVersion -}} +{{- $labels = mustMergeOverwrite $labels (dict "app.kubernetes.io/version" .Chart.AppVersion) -}} +{{- end -}} + +{{- $globalUserLabels := $global.labels | default dict -}} +{{- $localUserLabels := .Values.labels | default dict -}} + +{{- $labels = mustMergeOverwrite $labels $globalUserLabels $localUserLabels -}} + +{{- toYaml $labels -}} +{{- end -}} + + + +{{/* +This will render the labels that should be used in deployments/daemonsets template pods as a selector. +*/}} +{{- define "newrelic.common.labels.selectorLabels" -}} +{{- $name := dict "app.kubernetes.io/name" ( include "newrelic.common.naming.name" . ) -}} +{{- $instance := dict "app.kubernetes.io/instance" .Release.Name -}} + +{{- $selectorLabels := mustMergeOverwrite $name $instance -}} + +{{- toYaml $selectorLabels -}} +{{- end }} + + + +{{/* +Pod labels +*/}} +{{- define "newrelic.common.labels.podLabels" -}} +{{- $selectorLabels := fromYaml (include "newrelic.common.labels.selectorLabels" . ) -}} + +{{- $global := index .Values "global" | default dict -}} +{{- $globalPodLabels := $global.podLabels | default dict }} + +{{- $localPodLabels := .Values.podLabels | default dict }} + +{{- $podLabels := mustMergeOverwrite $selectorLabels $globalPodLabels $localPodLabels -}} + +{{- toYaml $podLabels -}} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_license.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_license.tpl new file mode 100644 index 0000000000..cb349f6bb6 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_license.tpl @@ -0,0 +1,68 @@ +{{/* +Return the name of the secret holding the License Key. +*/}} +{{- define "newrelic.common.license.secretName" -}} +{{- $default := include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "license" ) -}} +{{- include "newrelic.common.license._customSecretName" . | default $default -}} +{{- end -}} + +{{/* +Return the name key for the License Key inside the secret. +*/}} +{{- define "newrelic.common.license.secretKeyName" -}} +{{- include "newrelic.common.license._customSecretKey" . | default "licenseKey" -}} +{{- end -}} + +{{/* +Return local licenseKey if set, global otherwise. +This helper is for internal use. +*/}} +{{- define "newrelic.common.license._licenseKey" -}} +{{- if .Values.licenseKey -}} + {{- .Values.licenseKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.licenseKey -}} + {{- .Values.global.licenseKey -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name of the secret holding the License Key. +This helper is for internal use. +*/}} +{{- define "newrelic.common.license._customSecretName" -}} +{{- if .Values.customSecretName -}} + {{- .Values.customSecretName -}} +{{- else if .Values.global -}} + {{- if .Values.global.customSecretName -}} + {{- .Values.global.customSecretName -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name key for the License Key inside the secret. +This helper is for internal use. +*/}} +{{- define "newrelic.common.license._customSecretKey" -}} +{{- if .Values.customSecretLicenseKey -}} + {{- .Values.customSecretLicenseKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.customSecretLicenseKey }} + {{- .Values.global.customSecretLicenseKey -}} + {{- end -}} +{{- end -}} +{{- end -}} + + + +{{/* +Return empty string (falsehood) or "true" if the user set a custom secret for the license. +This helper is for internal use. +*/}} +{{- define "newrelic.common.license._usesCustomSecret" -}} +{{- if or (include "newrelic.common.license._customSecretName" .) (include "newrelic.common.license._customSecretKey" .) -}} +true +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_license_secret.yaml.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_license_secret.yaml.tpl new file mode 100644 index 0000000000..610a0a3370 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_license_secret.yaml.tpl @@ -0,0 +1,21 @@ +{{/* +Renders the license key secret if user has not specified a custom secret. +*/}} +{{- define "newrelic.common.license.secret" }} +{{- if not (include "newrelic.common.license._customSecretName" .) }} +{{- /* Fail if licenseKey is empty and required: */ -}} +{{- if not (include "newrelic.common.license._licenseKey" .) }} + {{- fail "You must specify a licenseKey or a customSecretName containing it" }} +{{- end }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "newrelic.common.license.secretName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +data: + {{ include "newrelic.common.license.secretKeyName" . }}: {{ include "newrelic.common.license._licenseKey" . | b64enc }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_low-data-mode.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_low-data-mode.tpl new file mode 100644 index 0000000000..3dd55ef2ff --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_low-data-mode.tpl @@ -0,0 +1,26 @@ +{{- /* +Abstraction of the lowDataMode toggle. +This helper allows to override the global `.global.lowDataMode` with the value of `.lowDataMode`. +Returns "true" if `lowDataMode` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.lowDataMode" -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if (get .Values "lowDataMode" | kindIs "bool") -}} + {{- if .Values.lowDataMode -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.lowDataMode" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.lowDataMode -}} + {{- end -}} +{{- else -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "lowDataMode" | kindIs "bool" -}} + {{- if $global.lowDataMode -}} + {{- $global.lowDataMode -}} + {{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_naming.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_naming.tpl new file mode 100644 index 0000000000..19fa92648c --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_naming.tpl @@ -0,0 +1,73 @@ +{{/* +This is an function to be called directly with a string just to truncate strings to +63 chars because some Kubernetes name fields are limited to that. +*/}} +{{- define "newrelic.common.naming.truncateToDNS" -}} +{{- . | trunc 63 | trimSuffix "-" }} +{{- end }} + + + +{{- /* +Given a name and a suffix returns a 'DNS Valid' which always include the suffix, truncating the name if needed. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If suffix is too long it gets truncated but it always takes precedence over name, so a 63 chars suffix would suppress the name. +Usage: +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" "" "suffix" "my-suffix" ) }} +*/ -}} +{{- define "newrelic.common.naming.truncateToDNSWithSuffix" -}} +{{- $suffix := (include "newrelic.common.naming.truncateToDNS" .suffix) -}} +{{- $maxLen := (max (sub 63 (add1 (len $suffix))) 0) -}} {{- /* We prepend "-" to the suffix so an additional character is needed */ -}} + +{{- $newName := .name | trunc ($maxLen | int) | trimSuffix "-" -}} +{{- if $newName -}} +{{- printf "%s-%s" $newName $suffix -}} +{{- else -}} +{{ $suffix }} +{{- end -}} + +{{- end -}} + + + +{{/* +Expand the name of the chart. +Uses the Chart name by default if nameOverride is not set. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "newrelic.common.naming.name" -}} +{{- $name := .Values.nameOverride | default .Chart.Name -}} +{{- include "newrelic.common.naming.truncateToDNS" $name -}} +{{- end }} + + + +{{/* +Create a default fully qualified app name. +By default the full name will be "" just in if it has the chart name included in that, if not +it will be concatenated like "-". This could change if fullnameOverride or +nameOverride are set. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "newrelic.common.naming.fullname" -}} +{{- $name := include "newrelic.common.naming.name" . -}} + +{{- if .Values.fullnameOverride -}} + {{- $name = .Values.fullnameOverride -}} +{{- else if not (contains $name .Release.Name) -}} + {{- $name = printf "%s-%s" .Release.Name $name -}} +{{- end -}} + +{{- include "newrelic.common.naming.truncateToDNS" $name -}} + +{{- end -}} + + + +{{/* +Create chart name and version as used by the chart label. +This function should not be used for naming objects. Use "common.naming.{name,fullname}" instead. +*/}} +{{- define "newrelic.common.naming.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_nodeselector.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_nodeselector.tpl new file mode 100644 index 0000000000..d488873412 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_nodeselector.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod nodeSelector */ -}} +{{- define "newrelic.common.nodeSelector" -}} + {{- if .Values.nodeSelector -}} + {{- toYaml .Values.nodeSelector -}} + {{- else if .Values.global -}} + {{- if .Values.global.nodeSelector -}} + {{- toYaml .Values.global.nodeSelector -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_priority-class-name.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_priority-class-name.tpl new file mode 100644 index 0000000000..50182b7343 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_priority-class-name.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the pod priorityClassName */ -}} +{{- define "newrelic.common.priorityClassName" -}} + {{- if .Values.priorityClassName -}} + {{- .Values.priorityClassName -}} + {{- else if .Values.global -}} + {{- if .Values.global.priorityClassName -}} + {{- .Values.global.priorityClassName -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_privileged.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_privileged.tpl new file mode 100644 index 0000000000..f3ae814dd9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_privileged.tpl @@ -0,0 +1,28 @@ +{{- /* +This is a helper that returns whether the chart should assume the user is fine deploying privileged pods. +*/ -}} +{{- define "newrelic.common.privileged" -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists. */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if get .Values "privileged" | kindIs "bool" -}} + {{- if .Values.privileged -}} + {{- .Values.privileged -}} + {{- end -}} +{{- else if get $global "privileged" | kindIs "bool" -}} + {{- if $global.privileged -}} + {{- $global.privileged -}} + {{- end -}} +{{- end -}} +{{- end -}} + + + +{{- /* Return directly "true" or "false" based in the exist of "newrelic.common.privileged" */ -}} +{{- define "newrelic.common.privileged.value" -}} +{{- if include "newrelic.common.privileged" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_proxy.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_proxy.tpl new file mode 100644 index 0000000000..60f34c7ec1 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_proxy.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the proxy */ -}} +{{- define "newrelic.common.proxy" -}} + {{- if .Values.proxy -}} + {{- .Values.proxy -}} + {{- else if .Values.global -}} + {{- if .Values.global.proxy -}} + {{- .Values.global.proxy -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_region.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_region.tpl new file mode 100644 index 0000000000..bdcacf3235 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_region.tpl @@ -0,0 +1,74 @@ +{{/* +Return the region that is being used by the user +*/}} +{{- define "newrelic.common.region" -}} +{{- if and (include "newrelic.common.license._usesCustomSecret" .) (not (include "newrelic.common.region._fromValues" .)) -}} + {{- fail "This Helm Chart is not able to compute the region. You must specify a .global.region or .region if the license is set using a custom secret." -}} +{{- end -}} + +{{- /* Defaults */ -}} +{{- $region := "us" -}} +{{- if include "newrelic.common.nrStaging" . -}} + {{- $region = "staging" -}} +{{- else if include "newrelic.common.region._isEULicenseKey" . -}} + {{- $region = "eu" -}} +{{- end -}} + +{{- include "newrelic.common.region.validate" (include "newrelic.common.region._fromValues" . | default $region ) -}} +{{- end -}} + + + +{{/* +Returns the region from the values if valid. This only return the value from the `values.yaml`. +More intelligence should be used to compute the region. + +Usage: `include "newrelic.common.region.validate" "us"` +*/}} +{{- define "newrelic.common.region.validate" -}} +{{- /* Ref: https://github.com/newrelic/newrelic-client-go/blob/cbe3e4cf2b95fd37095bf2ffdc5d61cffaec17e2/pkg/region/region_constants.go#L8-L21 */ -}} +{{- $region := . | lower -}} +{{- if eq $region "us" -}} + US +{{- else if eq $region "eu" -}} + EU +{{- else if eq $region "staging" -}} + Staging +{{- else if eq $region "local" -}} + Local +{{- else -}} + {{- fail (printf "the region provided is not valid: %s not in \"US\" \"EU\" \"Staging\" \"Local\"" .) -}} +{{- end -}} +{{- end -}} + + + +{{/* +Returns the region from the values. This only return the value from the `values.yaml`. +More intelligence should be used to compute the region. +This helper is for internal use. +*/}} +{{- define "newrelic.common.region._fromValues" -}} +{{- if .Values.region -}} + {{- .Values.region -}} +{{- else if .Values.global -}} + {{- if .Values.global.region -}} + {{- .Values.global.region -}} + {{- end -}} +{{- end -}} +{{- end -}} + + + +{{/* +Return empty string (falsehood) or "true" if the license is for EU region. +This helper is for internal use. +*/}} +{{- define "newrelic.common.region._isEULicenseKey" -}} +{{- if not (include "newrelic.common.license._usesCustomSecret" .) -}} + {{- $license := include "newrelic.common.license._licenseKey" . -}} + {{- if hasPrefix "eu" $license -}} + true + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_security-context.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_security-context.tpl new file mode 100644 index 0000000000..9edfcabfd0 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_security-context.tpl @@ -0,0 +1,23 @@ +{{- /* Defines the container securityContext context */ -}} +{{- define "newrelic.common.securityContext.container" -}} +{{- $global := index .Values "global" | default dict -}} + +{{- if .Values.containerSecurityContext -}} + {{- toYaml .Values.containerSecurityContext -}} +{{- else if $global.containerSecurityContext -}} + {{- toYaml $global.containerSecurityContext -}} +{{- end -}} +{{- end -}} + + + +{{- /* Defines the pod securityContext context */ -}} +{{- define "newrelic.common.securityContext.pod" -}} +{{- $global := index .Values "global" | default dict -}} + +{{- if .Values.podSecurityContext -}} + {{- toYaml .Values.podSecurityContext -}} +{{- else if $global.podSecurityContext -}} + {{- toYaml $global.podSecurityContext -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_serviceaccount.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_serviceaccount.tpl new file mode 100644 index 0000000000..2d352f6ea9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_serviceaccount.tpl @@ -0,0 +1,90 @@ +{{- /* Defines if the service account has to be created or not */ -}} +{{- define "newrelic.common.serviceAccount.create" -}} +{{- $valueFound := false -}} + +{{- /* Look for a global creation of a service account */ -}} +{{- if get .Values "serviceAccount" | kindIs "map" -}} + {{- if (get .Values.serviceAccount "create" | kindIs "bool") -}} + {{- $valueFound = true -}} + {{- if .Values.serviceAccount.create -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.serviceAccount.name" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.serviceAccount.create -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- /* Look for a local creation of a service account */ -}} +{{- if not $valueFound -}} + {{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} + {{- $global := index .Values "global" | default dict -}} + {{- if get $global "serviceAccount" | kindIs "map" -}} + {{- if get $global.serviceAccount "create" | kindIs "bool" -}} + {{- $valueFound = true -}} + {{- if $global.serviceAccount.create -}} + {{- $global.serviceAccount.create -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- /* In case no serviceAccount value has been found, default to "true" */ -}} +{{- if not $valueFound -}} +true +{{- end -}} +{{- end -}} + + + +{{- /* Defines the name of the service account */ -}} +{{- define "newrelic.common.serviceAccount.name" -}} +{{- $localServiceAccount := "" -}} +{{- if get .Values "serviceAccount" | kindIs "map" -}} + {{- if (get .Values.serviceAccount "name" | kindIs "string") -}} + {{- $localServiceAccount = .Values.serviceAccount.name -}} + {{- end -}} +{{- end -}} + +{{- $globalServiceAccount := "" -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "serviceAccount" | kindIs "map" -}} + {{- if get $global.serviceAccount "name" | kindIs "string" -}} + {{- $globalServiceAccount = $global.serviceAccount.name -}} + {{- end -}} +{{- end -}} + +{{- if (include "newrelic.common.serviceAccount.create" .) -}} + {{- $localServiceAccount | default $globalServiceAccount | default (include "newrelic.common.naming.fullname" .) -}} +{{- else -}} + {{- $localServiceAccount | default $globalServiceAccount | default "default" -}} +{{- end -}} +{{- end -}} + + + +{{- /* Merge the global and local annotations for the service account */ -}} +{{- define "newrelic.common.serviceAccount.annotations" -}} +{{- $localServiceAccount := dict -}} +{{- if get .Values "serviceAccount" | kindIs "map" -}} + {{- if get .Values.serviceAccount "annotations" -}} + {{- $localServiceAccount = .Values.serviceAccount.annotations -}} + {{- end -}} +{{- end -}} + +{{- $globalServiceAccount := dict -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "serviceAccount" | kindIs "map" -}} + {{- if get $global.serviceAccount "annotations" -}} + {{- $globalServiceAccount = $global.serviceAccount.annotations -}} + {{- end -}} +{{- end -}} + +{{- $merged := mustMergeOverwrite $globalServiceAccount $localServiceAccount -}} + +{{- if $merged -}} + {{- toYaml $merged -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_staging.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_staging.tpl new file mode 100644 index 0000000000..bd9ad09bb9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_staging.tpl @@ -0,0 +1,39 @@ +{{- /* +Abstraction of the nrStaging toggle. +This helper allows to override the global `.global.nrStaging` with the value of `.nrStaging`. +Returns "true" if `nrStaging` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.nrStaging" -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if (get .Values "nrStaging" | kindIs "bool") -}} + {{- if .Values.nrStaging -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.nrStaging" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.nrStaging -}} + {{- end -}} +{{- else -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "nrStaging" | kindIs "bool" -}} + {{- if $global.nrStaging -}} + {{- $global.nrStaging -}} + {{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} + + + +{{- /* +Returns "true" of "false" directly instead of empty string (Helm falsiness) based on the exit of "newrelic.common.nrStaging" +*/ -}} +{{- define "newrelic.common.nrStaging.value" -}} +{{- if include "newrelic.common.nrStaging" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_tolerations.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_tolerations.tpl new file mode 100644 index 0000000000..e016b38e27 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_tolerations.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod tolerations */ -}} +{{- define "newrelic.common.tolerations" -}} + {{- if .Values.tolerations -}} + {{- toYaml .Values.tolerations -}} + {{- else if .Values.global -}} + {{- if .Values.global.tolerations -}} + {{- toYaml .Values.global.tolerations -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_userkey.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_userkey.tpl new file mode 100644 index 0000000000..982ea8e09d --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_userkey.tpl @@ -0,0 +1,56 @@ +{{/* +Return the name of the secret holding the API Key. +*/}} +{{- define "newrelic.common.userKey.secretName" -}} +{{- $default := include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "userkey" ) -}} +{{- include "newrelic.common.userKey._customSecretName" . | default $default -}} +{{- end -}} + +{{/* +Return the name key for the API Key inside the secret. +*/}} +{{- define "newrelic.common.userKey.secretKeyName" -}} +{{- include "newrelic.common.userKey._customSecretKey" . | default "userKey" -}} +{{- end -}} + +{{/* +Return local API Key if set, global otherwise. +This helper is for internal use. +*/}} +{{- define "newrelic.common.userKey._userKey" -}} +{{- if .Values.userKey -}} + {{- .Values.userKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.userKey -}} + {{- .Values.global.userKey -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name of the secret holding the API Key. +This helper is for internal use. +*/}} +{{- define "newrelic.common.userKey._customSecretName" -}} +{{- if .Values.customUserKeySecretName -}} + {{- .Values.customUserKeySecretName -}} +{{- else if .Values.global -}} + {{- if .Values.global.customUserKeySecretName -}} + {{- .Values.global.customUserKeySecretName -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name key for the API Key inside the secret. +This helper is for internal use. +*/}} +{{- define "newrelic.common.userKey._customSecretKey" -}} +{{- if .Values.customUserKeySecretKey -}} + {{- .Values.customUserKeySecretKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.customUserKeySecretKey }} + {{- .Values.global.customUserKeySecretKey -}} + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_userkey_secret.yaml.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_userkey_secret.yaml.tpl new file mode 100644 index 0000000000..b979856548 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_userkey_secret.yaml.tpl @@ -0,0 +1,21 @@ +{{/* +Renders the user key secret if user has not specified a custom secret. +*/}} +{{- define "newrelic.common.userKey.secret" }} +{{- if not (include "newrelic.common.userKey._customSecretName" .) }} +{{- /* Fail if user key is empty and required: */ -}} +{{- if not (include "newrelic.common.userKey._userKey" .) }} + {{- fail "You must specify a userKey or a customUserKeySecretName containing it" }} +{{- end }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "newrelic.common.userKey.secretName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +data: + {{ include "newrelic.common.userKey.secretKeyName" . }}: {{ include "newrelic.common.userKey._userKey" . | b64enc }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_verbose-log.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_verbose-log.tpl new file mode 100644 index 0000000000..2286d46815 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/templates/_verbose-log.tpl @@ -0,0 +1,54 @@ +{{- /* +Abstraction of the verbose toggle. +This helper allows to override the global `.global.verboseLog` with the value of `.verboseLog`. +Returns "true" if `verbose` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.verboseLog" -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if (get .Values "verboseLog" | kindIs "bool") -}} + {{- if .Values.verboseLog -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.verboseLog" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.verboseLog -}} + {{- end -}} +{{- else -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "verboseLog" | kindIs "bool" -}} + {{- if $global.verboseLog -}} + {{- $global.verboseLog -}} + {{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} + + + +{{- /* +Abstraction of the verbose toggle. +This helper abstracts the function "newrelic.common.verboseLog" to return true or false directly. +*/ -}} +{{- define "newrelic.common.verboseLog.valueAsBoolean" -}} +{{- if include "newrelic.common.verboseLog" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} + + + +{{- /* +Abstraction of the verbose toggle. +This helper abstracts the function "newrelic.common.verboseLog" to return 1 or 0 directly. +*/ -}} +{{- define "newrelic.common.verboseLog.valueAsInt" -}} +{{- if include "newrelic.common.verboseLog" . -}} +1 +{{- else -}} +0 +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/values.yaml new file mode 100644 index 0000000000..75e2d112ad --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/charts/common-library/values.yaml @@ -0,0 +1 @@ +# values are not needed for the library chart, however this file is still needed for helm lint to work. diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/ci/test-values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/ci/test-values.yaml new file mode 100644 index 0000000000..f0f9be1f92 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/ci/test-values.yaml @@ -0,0 +1,14 @@ +global: + cluster: test-cluster + +personalAPIKey: "a21321" +verboseLog: false + +config: + accountID: 111 + region: EU + nrdbClientTimeoutSeconds: 30 + +image: + repository: e2e/newrelic-metrics-adapter + tag: "test" # Defaults to AppVersion diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/_helpers.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/_helpers.tpl new file mode 100644 index 0000000000..6a5f765037 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/_helpers.tpl @@ -0,0 +1,57 @@ +{{/* vim: set filetype=mustache: */}} + +{{- /* Allow to change pod defaults dynamically based if we are running in privileged mode or not */ -}} +{{- define "newrelic-k8s-metrics-adapter.securityContext.pod" -}} +{{- if include "newrelic.common.securityContext.pod" . -}} +{{- include "newrelic.common.securityContext.pod" . -}} +{{- else -}} +fsGroup: 1001 +runAsUser: 1001 +runAsGroup: 1001 +{{- end -}} +{{- end -}} + + + +{{/* +Select a value for the region +When this value is empty the New Relic client region will be the default 'US' +*/}} +{{- define "newrelic-k8s-metrics-adapter.region" -}} +{{- if .Values.config.region -}} + {{- .Values.config.region | upper -}} +{{- else if (include "newrelic.common.nrStaging" .) -}} +Staging +{{- else if hasPrefix "eu" (include "newrelic.common.license._licenseKey" .) -}} +EU +{{- end -}} +{{- end -}} + + + +{{- /* +Naming helpers +*/ -}} +{{- define "newrelic-k8s-metrics-adapter.name.apiservice" -}} +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "apiservice") }} +{{- end -}} + +{{- define "newrelic-k8s-metrics-adapter.name.apiservice.serviceAccount" -}} +{{- if include "newrelic.common.serviceAccount.create" . -}} + {{- include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "apiservice") -}} +{{- else -}} + {{- include "newrelic.common.serviceAccount.name" . -}} +{{- end -}} +{{- end -}} + +{{- define "newrelic-k8s-metrics-adapter.name.apiservice-create" -}} +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "apiservice-create") }} +{{- end -}} + +{{- define "newrelic-k8s-metrics-adapter.name.apiservice-patch" -}} +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "apiservice-patch") }} +{{- end -}} + +{{- define "newrelic-k8s-metrics-adapter.name.hpa-controller" -}} +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "hpa-controller") }} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/adapter-clusterrolebinding.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/adapter-clusterrolebinding.yaml new file mode 100644 index 0000000000..40bcba8b63 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/adapter-clusterrolebinding.yaml @@ -0,0 +1,14 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "newrelic.common.naming.fullname" . }}:system:auth-delegator + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:auth-delegator +subjects: +- kind: ServiceAccount + name: {{ include "newrelic.common.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/adapter-rolebinding.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/adapter-rolebinding.yaml new file mode 100644 index 0000000000..afb5d2d550 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/adapter-rolebinding.yaml @@ -0,0 +1,15 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "newrelic.common.naming.fullname" . }} + namespace: kube-system + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: extension-apiserver-authentication-reader +subjects: +- kind: ServiceAccount + name: {{ include "newrelic.common.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/apiservice.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/apiservice.yaml new file mode 100644 index 0000000000..8f01b64071 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/apiservice.yaml @@ -0,0 +1,19 @@ +apiVersion: apiregistration.k8s.io/v1 +kind: APIService +metadata: + name: v1beta1.external.metrics.k8s.io + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +{{- if .Values.certManager.enabled }} + annotations: + certmanager.k8s.io/inject-ca-from: {{ printf "%s/%s-root-cert" .Release.Namespace (include "newrelic.common.naming.fullname" .) | quote }} + cert-manager.io/inject-ca-from: {{ printf "%s/%s-root-cert" .Release.Namespace (include "newrelic.common.naming.fullname" .) | quote }} +{{- end }} +spec: + service: + name: {{ include "newrelic.common.naming.fullname" . }} + namespace: {{ .Release.Namespace }} + group: external.metrics.k8s.io + version: v1beta1 + groupPriorityMinimum: 100 + versionPriority: 100 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/clusterrole.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/clusterrole.yaml new file mode 100644 index 0000000000..5c364eb375 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/clusterrole.yaml @@ -0,0 +1,26 @@ +{{- if (and (not .Values.customTLSCertificate) (not .Values.certManager.enabled)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "newrelic-k8s-metrics-adapter.name.apiservice" . }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +rules: + - apiGroups: + - apiregistration.k8s.io + resources: + - apiservices + verbs: + - get + - update +{{- if .Values.rbac.pspEnabled }} + - apiGroups: ['policy'] + resources: ['podsecuritypolicies'] + verbs: ['use'] + resourceNames: + - {{ include "newrelic-k8s-metrics-adapter.name.apiservice" . }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/clusterrolebinding.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/clusterrolebinding.yaml new file mode 100644 index 0000000000..8aa95792e8 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/clusterrolebinding.yaml @@ -0,0 +1,19 @@ +{{- if (and (not .Values.customTLSCertificate) (not .Values.certManager.enabled)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "newrelic-k8s-metrics-adapter.name.apiservice" . }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "newrelic-k8s-metrics-adapter.name.apiservice" . }} +subjects: + - kind: ServiceAccount + name: {{ include "newrelic-k8s-metrics-adapter.name.apiservice.serviceAccount" . }} + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/job-createSecret.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/job-createSecret.yaml new file mode 100644 index 0000000000..6cf89b79e6 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/job-createSecret.yaml @@ -0,0 +1,55 @@ +{{- if (and (not .Values.customTLSCertificate) (not .Values.certManager.enabled)) }} +apiVersion: batch/v1 +kind: Job +metadata: + namespace: {{ .Release.Namespace }} + name: {{ include "newrelic-k8s-metrics-adapter.name.apiservice-create" . }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +spec: + template: + metadata: + name: {{ include "newrelic-k8s-metrics-adapter.name.apiservice-create" . }} + labels: + {{- include "newrelic.common.labels" . | nindent 8 }} + spec: + {{- with include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" (list .Values.image.pullSecrets) "context" .) }} + imagePullSecrets: + {{- . | nindent 8 }} + {{- end }} + containers: + - name: create + image: {{ include "newrelic.common.images.image" ( dict "defaultRegistry" "registry.k8s.io" "imageRoot" .Values.apiServicePatchJob.image "context" .) }} + imagePullPolicy: {{ .Values.apiServicePatchJob.image.pullPolicy }} + args: + - create + - --host={{ include "newrelic.common.naming.fullname" . }},{{ include "newrelic.common.naming.fullname" . }}.{{ .Release.Namespace }}.svc + - --namespace={{ .Release.Namespace }} + - --secret-name={{ include "newrelic-k8s-metrics-adapter.name.apiservice" . }} + - --cert-name=tls.crt + - --key-name=tls.key + {{- with .Values.apiServicePatchJob.volumeMounts }} + volumeMounts: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.apiServicePatchJob.volumes }} + volumes: + {{- toYaml . | nindent 8 }} + {{- end }} + restartPolicy: OnFailure + serviceAccountName: {{ include "newrelic-k8s-metrics-adapter.name.apiservice.serviceAccount" . }} + securityContext: + runAsGroup: 2000 + runAsNonRoot: true + runAsUser: 2000 + nodeSelector: + kubernetes.io/os: linux + {{ include "newrelic.common.nodeSelector" . | nindent 8 }} + {{- with include "newrelic.common.tolerations" . }} + tolerations: + {{- . | nindent 8 }} + {{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/job-patchAPIService.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/job-patchAPIService.yaml new file mode 100644 index 0000000000..9d651c2108 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/job-patchAPIService.yaml @@ -0,0 +1,53 @@ +{{- if (and (not .Values.customTLSCertificate) (not .Values.certManager.enabled)) }} +apiVersion: batch/v1 +kind: Job +metadata: + namespace: {{ .Release.Namespace }} + name: {{ include "newrelic-k8s-metrics-adapter.name.apiservice-patch" . }} + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +spec: + template: + metadata: + name: {{ include "newrelic-k8s-metrics-adapter.name.apiservice-patch" . }} + labels: + {{- include "newrelic.common.labels" . | nindent 8 }} + spec: + {{- with include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" (list .Values.image.pullSecrets) "context" .) }} + imagePullSecrets: + {{- . | nindent 8 }} + {{- end }} + containers: + - name: patch + image: {{ include "newrelic.common.images.image" ( dict "defaultRegistry" "registry.k8s.io" "imageRoot" .Values.apiServicePatchJob.image "context" .) }} + imagePullPolicy: {{ .Values.apiServicePatchJob.image.pullPolicy }} + args: + - patch + - --namespace={{ .Release.Namespace }} + - --secret-name={{ include "newrelic-k8s-metrics-adapter.name.apiservice" . }} + - --apiservice-name=v1beta1.external.metrics.k8s.io + {{- with .Values.apiServicePatchJob.volumeMounts }} + volumeMounts: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.apiServicePatchJob.volumes }} + volumes: + {{- toYaml . | nindent 6 }} + {{- end }} + restartPolicy: OnFailure + serviceAccountName: {{ include "newrelic-k8s-metrics-adapter.name.apiservice.serviceAccount" . }} + securityContext: + runAsGroup: 2000 + runAsNonRoot: true + runAsUser: 2000 + nodeSelector: + kubernetes.io/os: linux + {{ include "newrelic.common.nodeSelector" . | nindent 8 }} + {{- with include "newrelic.common.tolerations" . }} + tolerations: + {{- . | nindent 8 }} + {{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/psp.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/psp.yaml new file mode 100644 index 0000000000..1dd6bc1a6f --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/psp.yaml @@ -0,0 +1,49 @@ +{{- if (and (not .Values.customTLSCertificate) (not .Values.certManager.enabled) (.Values.rbac.pspEnabled)) }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ include "newrelic-k8s-metrics-adapter.name.apiservice" . }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +spec: + privileged: false + # Required to prevent escalations to root. + # allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + # requiredDropCapabilities: + # - ALL + # Allow core volume types. + volumes: + - 'configMap' + - 'emptyDir' + - 'projected' + - 'secret' + - 'downwardAPI' + - 'persistentVolumeClaim' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + # Permits the container to run with root privileges as well. + rule: 'RunAsAny' + seLinux: + # This policy assumes the nodes are using AppArmor rather than SELinux. + rule: 'RunAsAny' + supplementalGroups: + rule: 'MustRunAs' + ranges: + # Forbid adding the root group. + - min: 0 + max: 65535 + fsGroup: + rule: 'MustRunAs' + ranges: + # Forbid adding the root group. + - min: 0 + max: 65535 + readOnlyRootFilesystem: false +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/role.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/role.yaml new file mode 100644 index 0000000000..1e870e0828 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/role.yaml @@ -0,0 +1,20 @@ +{{- if (and (not .Values.customTLSCertificate) (not .Values.certManager.enabled)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + namespace: {{ .Release.Namespace }} + name: {{ include "newrelic-k8s-metrics-adapter.name.apiservice" . }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +rules: + - apiGroups: + - "" + resources: + - secrets + verbs: + - get + - create +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/rolebinding.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/rolebinding.yaml new file mode 100644 index 0000000000..cbe8bdb72d --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/rolebinding.yaml @@ -0,0 +1,20 @@ +{{- if (and (not .Values.customTLSCertificate) (not .Values.certManager.enabled)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + namespace: {{ .Release.Namespace }} + name: {{ include "newrelic-k8s-metrics-adapter.name.apiservice" . }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "newrelic-k8s-metrics-adapter.name.apiservice" . }} +subjects: + - kind: ServiceAccount + name: {{ include "newrelic-k8s-metrics-adapter.name.apiservice.serviceAccount" . }} + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/serviceaccount.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/serviceaccount.yaml new file mode 100644 index 0000000000..68a3cfd730 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/apiservice/job-patch/serviceaccount.yaml @@ -0,0 +1,18 @@ +{{- $createServiceAccount := include "newrelic.common.serviceAccount.create" . -}} +{{- if (and $createServiceAccount (not .Values.customTLSCertificate) (not .Values.certManager.enabled)) -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + namespace: {{ .Release.Namespace }} + name: {{ include "newrelic-k8s-metrics-adapter.name.apiservice.serviceAccount" . }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + # When hooks are sorted by weight and name, kind order gets overwritten, + # then this serviceAccount doesn't get created before dependent objects causing a failure. + # This weight is set, forcing it always to get created before the other objects. + # We submitted this PR to fix the issue: https://github.com/helm/helm/pull/10787 + "helm.sh/hook-weight": "-1" + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/configmap.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/configmap.yaml new file mode 100644 index 0000000000..8e88ad59e4 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/configmap.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + namespace: {{ .Release.Namespace }} + name: {{ include "newrelic.common.naming.fullname" . }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +data: + config.yaml: | + accountID: {{ .Values.config.accountID | required "config.accountID is required" }} + {{- with (include "newrelic-k8s-metrics-adapter.region" .) }} + region: {{ . }} + {{- end }} + cacheTTLSeconds: {{ .Values.config.cacheTTLSeconds | default "0" }} + {{- with .Values.config.externalMetrics }} + externalMetrics: + {{- toYaml . | nindent 6 }} + {{- end }} + nrdbClientTimeoutSeconds: {{ .Values.config.nrdbClientTimeoutSeconds | default "30" }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/deployment.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/deployment.yaml new file mode 100644 index 0000000000..1b96459a5d --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/deployment.yaml @@ -0,0 +1,113 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: {{ .Release.Namespace }} + name: {{ include "newrelic.common.naming.fullname" . }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicas }} + selector: + matchLabels: + {{- include "newrelic.common.labels.selectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }} + {{- if .Values.podAnnotations }} + {{- toYaml .Values.podAnnotations | nindent 8 }} + {{- end }} + labels: + {{- include "newrelic.common.labels.podLabels" . | nindent 8 }} + spec: + serviceAccountName: {{ include "newrelic.common.serviceAccount.name" . }} + {{- with include "newrelic-k8s-metrics-adapter.securityContext.pod" . }} + securityContext: + {{- . | nindent 8 }} + {{- end }} + {{- with include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" (list .Values.image.pullSecrets) "context" .) }} + imagePullSecrets: + {{- . | nindent 8 }} + {{- end }} + containers: + - name: {{ include "newrelic.common.naming.name" . }} + image: {{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.image "context" .) }} + imagePullPolicy: "{{ .Values.image.pullPolicy }}" + {{- with include "newrelic.common.securityContext.container" . }} + securityContext: + {{- . | nindent 10 }} + {{- end }} + args: + - --tls-cert-file=/tmp/k8s-metrics-adapter/serving-certs/tls.crt + - --tls-private-key-file=/tmp/k8s-metrics-adapter/serving-certs/tls.key + {{- if .Values.verboseLog }} + - --v=10 + {{- else }} + - --v=1 + {{- end }} + readinessProbe: + httpGet: + scheme: HTTPS + path: /healthz + port: 6443 + initialDelaySeconds: 1 + {{- if .Values.resources }} + resources: + {{- toYaml .Values.resources | nindent 10 }} + {{- end }} + env: + - name: CLUSTER_NAME + value: {{ include "newrelic.common.cluster" . }} + - name: NEWRELIC_API_KEY + valueFrom: + secretKeyRef: + name: {{ include "newrelic.common.naming.fullname" . }} + key: personalAPIKey + {{- with (include "newrelic.common.proxy" .) }} + - name: HTTPS_PROXY + value: {{ . }} + {{- end }} + {{- with .Values.extraEnv }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.extraEnvFrom }} + envFrom: {{ toYaml . | nindent 8 }} + {{- end }} + volumeMounts: + - name: tls-key-cert-pair + mountPath: /tmp/k8s-metrics-adapter/serving-certs/ + - name: config + mountPath: /etc/newrelic/adapter/ + {{- with .Values.extraVolumeMounts }} + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + - name: tls-key-cert-pair + secret: + secretName: {{ include "newrelic-k8s-metrics-adapter.name.apiservice" . }} + - name: config + configMap: + name: {{ include "newrelic.common.naming.fullname" . }} + {{- with .Values.extraVolumes }} + {{- toYaml . | nindent 6 }} + {{- end }} + {{- with include "newrelic.common.priorityClassName" . }} + priorityClassName: {{ . }} + {{- end }} + nodeSelector: + kubernetes.io/os: linux + {{ include "newrelic.common.nodeSelector" . | nindent 8 }} + {{- with include "newrelic.common.tolerations" . }} + tolerations: + {{- . | nindent 8 }} + {{- end }} + {{- with include "newrelic.common.affinity" . }} + affinity: + {{- . | nindent 8 }} + {{- end }} + hostNetwork: {{ include "newrelic.common.hostNetwork.value" . }} + {{- with include "newrelic.common.dnsConfig" . }} + dnsConfig: + {{- . | nindent 8 }} + {{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/hpa-clusterrole.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/hpa-clusterrole.yaml new file mode 100644 index 0000000000..402fece01e --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/hpa-clusterrole.yaml @@ -0,0 +1,15 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "newrelic.common.naming.fullname" . }}:external-metrics + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +rules: +- apiGroups: + - external.metrics.k8s.io + resources: + - "*" + verbs: + - list + - get + - watch diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/hpa-clusterrolebinding.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/hpa-clusterrolebinding.yaml new file mode 100644 index 0000000000..390fab452b --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/hpa-clusterrolebinding.yaml @@ -0,0 +1,14 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "newrelic-k8s-metrics-adapter.name.hpa-controller" . }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "newrelic.common.naming.fullname" . }}:external-metrics +subjects: +- kind: ServiceAccount + name: horizontal-pod-autoscaler + namespace: kube-system diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/secret.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/secret.yaml new file mode 100644 index 0000000000..09a70ab655 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/secret.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Secret +metadata: + namespace: {{ .Release.Namespace }} + name: {{ include "newrelic.common.naming.fullname" . }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +type: Opaque +stringData: + personalAPIKey: {{ .Values.personalAPIKey | required "personalAPIKey must be set" | quote }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/service.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/service.yaml new file mode 100644 index 0000000000..82015830c6 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + namespace: {{ .Release.Namespace }} + name: {{ include "newrelic.common.naming.fullname" . }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +spec: + ports: + - port: 443 + targetPort: 6443 + selector: + {{- include "newrelic.common.labels.selectorLabels" . | nindent 4 }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/serviceaccount.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/serviceaccount.yaml new file mode 100644 index 0000000000..b1e74523e5 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if include "newrelic.common.serviceAccount.create" . -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + {{- if include "newrelic.common.serviceAccount.annotations" . }} + annotations: + {{- include "newrelic.common.serviceAccount.annotations" . | nindent 4 }} + {{- end }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "newrelic.common.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/apiservice_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/apiservice_test.yaml new file mode 100644 index 0000000000..086160edc5 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/apiservice_test.yaml @@ -0,0 +1,22 @@ +suite: test naming helper for APIService's certmanager annotations and service name +templates: + - templates/apiservice/apiservice.yaml +release: + name: my-release + namespace: my-namespace +tests: + - it: Annotations are correctly defined + set: + personalAPIKey: 21321 + cluster: test-cluster + config: + accountID: 11111111 + certManager: + enabled: true + asserts: + - matchRegex: + path: metadata.annotations["certmanager.k8s.io/inject-ca-from"] + pattern: ^my-namespace\/.*-root-cert + - matchRegex: + path: metadata.annotations["cert-manager.io/inject-ca-from"] + pattern: ^my-namespace\/.*-root-cert diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/common_extra_naming_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/common_extra_naming_test.yaml new file mode 100644 index 0000000000..82098ba1c4 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/common_extra_naming_test.yaml @@ -0,0 +1,27 @@ +suite: test naming helpers +templates: + - templates/adapter-clusterrolebinding.yaml + - templates/hpa-clusterrole.yaml + - templates/hpa-clusterrolebinding.yaml + - templates/apiservice/job-patch/clusterrole.yaml + - templates/apiservice/job-patch/clusterrolebinding.yaml + - templates/apiservice/job-patch/job-createSecret.yaml + - templates/apiservice/job-patch/job-patchAPIService.yaml + - templates/apiservice/job-patch/psp.yaml + - templates/apiservice/job-patch/rolebinding.yaml +release: + name: my-release + namespace: my-namespace +tests: + - it: default values has its name correctly defined + set: + cluster: test-cluster + personalAPIKey: 21321 + config: + accountID: 11111111 + rbac: + pspEnabled: true + asserts: + - matchRegex: + path: metadata.name + pattern: ^.*(-apiservice|-hpa-controller|:external-metrics|:system:auth-delegator) diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/configmap_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/configmap_test.yaml new file mode 100644 index 0000000000..90b8798a7b --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/configmap_test.yaml @@ -0,0 +1,104 @@ +suite: test configmap region helper and externalMetrics +templates: + - templates/configmap.yaml +release: + name: my-release + namespace: my-namespace +tests: + - it: has the correct region when defined in local values + set: + personalAPIKey: 21321 + cluster: test-cluster + config: + accountID: 111 + region: A-REGION + asserts: + - equal: + path: data["config.yaml"] + value: | + accountID: 111 + region: A-REGION + cacheTTLSeconds: 30 + nrdbClientTimeoutSeconds: 30 + - it: has the correct region when global staging + set: + personalAPIKey: 21321 + cluster: test-cluster + config: + accountID: 111 + global: + nrStaging: true + asserts: + - equal: + path: data["config.yaml"] + value: | + accountID: 111 + region: Staging + cacheTTLSeconds: 30 + nrdbClientTimeoutSeconds: 30 + - it: has the correct region when global values and licenseKey is from eu + set: + personalAPIKey: 21321 + licenseKey: eu-whatever + cluster: test-cluster + config: + accountID: 111 + global: + aRandomGlobalValue: true + asserts: + - equal: + path: data["config.yaml"] + value: | + accountID: 111 + region: EU + cacheTTLSeconds: 30 + nrdbClientTimeoutSeconds: 30 + - it: has the correct region when no global values exist and licenseKey is from eu + set: + personalAPIKey: 21321 + cluster: test-cluster + licenseKey: eu-whatever + config: + accountID: 111 + asserts: + - equal: + path: data["config.yaml"] + value: | + accountID: 111 + region: EU + cacheTTLSeconds: 30 + nrdbClientTimeoutSeconds: 30 + - it: has no region when not defined and licenseKey is not from eu + set: + personalAPIKey: 21321 + cluster: test-cluster + licenseKey: us-whatever + config: + accountID: 111 + asserts: + - equal: + path: data["config.yaml"] + value: | + accountID: 111 + cacheTTLSeconds: 30 + nrdbClientTimeoutSeconds: 30 + - it: has externalMetrics when defined + set: + personalAPIKey: 21321 + cluster: test-cluster + licenseKey: us-whatever + config: + accountID: 111 + externalMetrics: + nginx_average_requests: + query: "FROM Metric SELECT average(nginx.server.net.requestsPerSecond)" + asserts: + - equal: + path: data["config.yaml"] + value: | + accountID: 111 + cacheTTLSeconds: 30 + externalMetrics: + nginx_average_requests: + query: FROM Metric SELECT average(nginx.server.net.requestsPerSecond) + nrdbClientTimeoutSeconds: 30 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/deployment_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/deployment_test.yaml new file mode 100644 index 0000000000..7a18987907 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/deployment_test.yaml @@ -0,0 +1,99 @@ +suite: test deployent images +release: + name: my-release + namespace: my-namespace +tests: + - it: has the correct image + set: + global: + cluster: test-cluster + personalAPIKey: 21321 + image: + repository: newrelic/newrelic-k8s-metrics-adapter + tag: "latest" + pullSecrets: + - name: regsecret + config: + accountID: 111 + region: A-REGION + asserts: + - matchRegex: + path: spec.template.spec.containers[0].image + pattern: ^.*newrelic/newrelic-k8s-metrics-adapter:latest + template: templates/deployment.yaml + - equal: + path: spec.template.spec.imagePullSecrets + value: + - name: regsecret + template: templates/deployment.yaml + - it: correctly uses the cluster helper + set: + personalAPIKey: 21321 + config: + accountID: 111 + region: A-REGION + cluster: a-cluster + asserts: + - equal: + path: spec.template.spec.containers[0].env[0].value + value: a-cluster + template: templates/deployment.yaml + - it: correctly uses common.securityContext.podDefaults + set: + personalAPIKey: 21321 + config: + accountID: 111 + region: A-REGION + cluster: a-cluster + asserts: + - equal: + path: spec.template.spec.securityContext + value: + fsGroup: 1001 + runAsGroup: 1001 + runAsUser: 1001 + template: templates/deployment.yaml + - it: correctly uses common.proxy + set: + personalAPIKey: 21321 + config: + accountID: 111 + region: A-REGION + cluster: a-cluster + proxy: localhost:1234 + asserts: + - equal: + path: spec.template.spec.containers[0].env[2].value + value: localhost:1234 + template: templates/deployment.yaml + + - it: has a linux node selector by default + set: + personalAPIKey: 21321 + cluster: test-cluster + config: + accountID: 111 + region: A-REGION + asserts: + - equal: + path: spec.template.spec.nodeSelector + value: + kubernetes.io/os: linux + template: templates/deployment.yaml + + - it: has a linux node selector and additional selectors + set: + personalAPIKey: 21321 + cluster: test-cluster + config: + accountID: 111 + region: A-REGION + nodeSelector: + aCoolTestLabel: aCoolTestValue + asserts: + - equal: + path: spec.template.spec.nodeSelector + value: + kubernetes.io/os: linux + aCoolTestLabel: aCoolTestValue + template: templates/deployment.yaml diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/hpa_clusterrolebinding_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/hpa_clusterrolebinding_test.yaml new file mode 100644 index 0000000000..4fba87fbe1 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/hpa_clusterrolebinding_test.yaml @@ -0,0 +1,18 @@ +suite: test naming helper for clusterRolebBinding roleRef +templates: + - templates/hpa-clusterrolebinding.yaml +release: + name: my-release + namespace: my-namespace +tests: + - it: roleRef.name has its name correctly defined + set: + personalAPIKey: 21321 + cluster: test-cluster + config: + accountID: 111 + region: A-REGION + asserts: + - matchRegex: + path: roleRef.name + pattern: ^.*:external-metrics diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/job_patch_cluster_rolebinding_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/job_patch_cluster_rolebinding_test.yaml new file mode 100644 index 0000000000..dd582313ea --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/job_patch_cluster_rolebinding_test.yaml @@ -0,0 +1,22 @@ +suite: test job-patch RoleBinding and ClusterRoleBinding rendering and roleRef/Subjects names +templates: + - templates/apiservice/job-patch/rolebinding.yaml + - templates/apiservice/job-patch/clusterrolebinding.yaml +release: + name: my-release + namespace: my-namespace +tests: + - it: roleRef apiGroup and Subjets are correctly defined + set: + personalAPIKey: 21321 + cluster: test-cluster + config: + accountID: 111 + region: A-REGION + asserts: + - matchRegex: + path: roleRef.name + pattern: ^.*-apiservice + - matchRegex: + path: subjects[0].name + pattern: ^.*-apiservice diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/job_patch_clusterrole_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/job_patch_clusterrole_test.yaml new file mode 100644 index 0000000000..33a1eaa731 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/job_patch_clusterrole_test.yaml @@ -0,0 +1,20 @@ +suite: test job-patch clusterRole rule resourceName and rendering +templates: + - templates/apiservice/job-patch/clusterrole.yaml +release: + name: my-release + namespace: my-namespace +tests: + - it: PodSecurityPolicy rule resourceName is correctly defined + set: + rbac: + pspEnabled: true + personalAPIKey: 21321 + cluster: test-cluster + config: + accountID: 111 + region: A-REGION + asserts: + - matchRegex: + path: rules[1].resourceNames[0] + pattern: ^.*-apiservice diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/job_patch_common_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/job_patch_common_test.yaml new file mode 100644 index 0000000000..91cd791d1c --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/job_patch_common_test.yaml @@ -0,0 +1,27 @@ +suite: test labels and rendering for job-batch objects +templates: + - templates/apiservice/job-patch/clusterrole.yaml + - templates/apiservice/job-patch/clusterrolebinding.yaml + - templates/apiservice/job-patch/job-createSecret.yaml + - templates/apiservice/job-patch/job-patchAPIService.yaml + - templates/apiservice/job-patch/psp.yaml + - templates/apiservice/job-patch/role.yaml + - templates/apiservice/job-patch/rolebinding.yaml + - templates/apiservice/job-patch/serviceaccount.yaml +release: + name: my-release + namespace: my-namespace +tests: + - it: If customTLSCertificate and Certmanager enabled do not render + set: + personalAPIKey: 21321 + cluster: test-cluster + config: + accountID: 111 + region: A-REGION + customTLSCertificate: a-tls-cert + certManager: + enabled: true + asserts: + - hasDocuments: + count: 0 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/job_patch_job_createsecret_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/job_patch_job_createsecret_test.yaml new file mode 100644 index 0000000000..6db79234fd --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/job_patch_job_createsecret_test.yaml @@ -0,0 +1,47 @@ +suite: test naming helper for job-createSecret +templates: + - templates/apiservice/job-patch/job-createSecret.yaml +release: + name: my-release + namespace: my-namespace +tests: + - it: spec metadata name is is correctly defined + set: + personalAPIKey: 21321 + cluster: test-cluster + config: + accountID: 111 + region: A-REGION + asserts: + - equal: + path: spec.template.metadata.name + value: my-release-newrelic-k8s-metrics-adapter-apiservice-create + - it: container args are correctly defined + set: + personalAPIKey: 21321 + cluster: test-cluster + config: + accountID: 111 + region: A-REGION + asserts: + - matchRegex: + path: spec.template.spec.containers[0].args[1] + pattern: --host=.*,.*\.my-namespace.svc + - matchRegex: + path: spec.template.spec.containers[0].args[3] + pattern: --secret-name=.*-apiservice + - it: has the correct image + set: + cluster: test-cluster + config: + accountID: 111 + region: A-REGION + personalAPIKey: 21321 + apiServicePatchJob: + image: + repository: registry.k8s.io/ingress-nginx/kube-webhook-certgen + tag: "latest" + asserts: + - matchRegex: + path: spec.template.spec.containers[0].image + pattern: ^.*registry.k8s.io/ingress-nginx/kube-webhook-certgen:latest diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/job_patch_job_patchapiservice_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/job_patch_job_patchapiservice_test.yaml new file mode 100644 index 0000000000..0be0833135 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/job_patch_job_patchapiservice_test.yaml @@ -0,0 +1,56 @@ +suite: test naming helper for job-patchAPIService +templates: + - templates/apiservice/job-patch/job-patchAPIService.yaml +release: + name: my-release + namespace: my-namespace +tests: + - it: spec metadata name is is correctly defined + set: + personalAPIKey: 21321 + cluster: test-cluster + config: + accountID: 111 + region: A-REGION + asserts: + - matchRegex: + path: spec.template.metadata.name + pattern: .*-apiservice-patch$ + - it: container args are correctly defined + set: + personalAPIKey: 21321 + cluster: test-cluster + config: + accountID: 111 + region: A-REGION + asserts: + - matchRegex: + path: spec.template.spec.containers[0].args[2] + pattern: ^--secret-name=.*-apiservice + + - it: serviceAccountName is correctly defined + set: + personalAPIKey: 21321 + cluster: test-cluster + config: + accountID: 111 + region: A-REGION + asserts: + - matchRegex: + path: spec.template.spec.serviceAccountName + pattern: .*-apiservice$ + - it: has the correct image + set: + personalAPIKey: 21321 + cluster: test-cluster + config: + accountID: 111 + region: A-REGION + apiServicePatchJob: + image: + repository: registry.k8s.io/ingress-nginx/kube-webhook-certgen + tag: "latest" + asserts: + - matchRegex: + path: spec.template.spec.containers[0].image + pattern: .*registry.k8s.io/ingress-nginx/kube-webhook-certgen:latest$ diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/job_serviceaccount_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/job_serviceaccount_test.yaml new file mode 100644 index 0000000000..9b6207c350 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/job_serviceaccount_test.yaml @@ -0,0 +1,79 @@ +suite: test job' serviceAccount +templates: + - templates/apiservice/job-patch/job-createSecret.yaml + - templates/apiservice/job-patch/job-patchAPIService.yaml +release: + name: my-release + namespace: my-namespace +tests: + - it: RBAC points to the service account that is created by default + set: + personalAPIKey: 21321 + cluster: test-cluster + config: + accountID: 111 + region: A-REGION + rbac.create: true + serviceAccount.create: true + asserts: + - equal: + path: spec.template.spec.serviceAccountName + value: my-release-newrelic-k8s-metrics-adapter-apiservice + + - it: RBAC points to the service account the user supplies when serviceAccount is disabled + set: + personalAPIKey: 21321 + cluster: test-cluster + config: + accountID: 111 + region: A-REGION + rbac.create: true + serviceAccount.create: false + serviceAccount.name: sa-test + asserts: + - equal: + path: spec.template.spec.serviceAccountName + value: sa-test + + - it: RBAC points to the service account the user supplies when serviceAccount is disabled + set: + personalAPIKey: 21321 + cluster: test-cluster + config: + accountID: 111 + region: A-REGION + rbac.create: true + serviceAccount.create: false + asserts: + - equal: + path: spec.template.spec.serviceAccountName + value: default + + - it: has a linux node selector by default + set: + personalAPIKey: 21321 + cluster: test-cluster + config: + accountID: 111 + region: A-REGION + asserts: + - equal: + path: spec.template.spec.nodeSelector + value: + kubernetes.io/os: linux + + - it: has a linux node selector and additional selectors + set: + personalAPIKey: 21321 + cluster: test-cluster + config: + accountID: 111 + region: A-REGION + nodeSelector: + aCoolTestLabel: aCoolTestValue + asserts: + - equal: + path: spec.template.spec.nodeSelector + value: + kubernetes.io/os: linux + aCoolTestLabel: aCoolTestValue \ No newline at end of file diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/rbac_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/rbac_test.yaml new file mode 100644 index 0000000000..78884c0229 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/tests/rbac_test.yaml @@ -0,0 +1,50 @@ +suite: test RBAC creation +templates: + - templates/apiservice/job-patch/rolebinding.yaml + - templates/apiservice/job-patch/clusterrolebinding.yaml +release: + name: my-release + namespace: my-namespace +tests: + - it: RBAC points to the service account that is created by default + set: + personalAPIKey: 21321 + cluster: test-cluster + config: + accountID: 111 + region: A-REGION + rbac.create: true + serviceAccount.create: true + asserts: + - equal: + path: subjects[0].name + value: my-release-newrelic-k8s-metrics-adapter-apiservice + + - it: RBAC points to the service account the user supplies when serviceAccount is disabled + set: + personalAPIKey: 21321 + cluster: test-cluster + config: + accountID: 111 + region: A-REGION + rbac.create: true + serviceAccount.create: false + serviceAccount.name: sa-test + asserts: + - equal: + path: subjects[0].name + value: sa-test + + - it: RBAC points to the service account the user supplies when serviceAccount is disabled + set: + personalAPIKey: 21321 + cluster: test-cluster + config: + accountID: 111 + region: A-REGION + rbac.create: true + serviceAccount.create: false + asserts: + - equal: + path: subjects[0].name + value: default diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/values.yaml new file mode 100644 index 0000000000..5c610f7926 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-k8s-metrics-adapter/values.yaml @@ -0,0 +1,156 @@ +# IMPORTANT: The Kubernetes cluster name +# https://docs.newrelic.com/docs/kubernetes-monitoring-integration +# +# licenseKey: +# cluster: +# IMPORTANT: the previous values can also be set as global so that they +# can be shared by other newrelic product's charts. +# +# global: +# licenseKey: +# cluster: +# nrStaging: + +# -- New Relic [Personal API Key](https://docs.newrelic.com/docs/apis/intro-apis/new-relic-api-keys/#user-api-key) (stored in a secret). Used to connect to NerdGraph in order to fetch the configured metrics. (**Required**) +personalAPIKey: + +# -- Enable metrics adapter verbose logs. +verboseLog: false + +config: + # -- New Relic [Account ID](https://docs.newrelic.com/docs/accounts/accounts-billing/account-structure/account-id/) where the configured metrics are sourced from. (**Required**) + accountID: + + # config.region -- New Relic account region. If not set, it will be automatically derived from the License Key. + # @default -- Automatically detected from `licenseKey`. + region: + # For US-based accounts, the region is: `US`. + # For EU-based accounts, the region is: `EU`. + # For Staging accounts, the region is: 'Staging' this is also automatically derived form `global.nrStaging` + + + # config.cacheTTLSeconds -- Period of time in seconds in which a cached value of a metric is consider valid. + cacheTTLSeconds: 30 + # Not setting it or setting it to '0' disables the cache. + + # config.externalMetrics -- Contains all the external metrics definition of the adapter. Each key of the externalMetric entry represents the metric name and contains the parameters that defines it. + # @default -- See `values.yaml` + externalMetrics: + # Names cannot contain uppercase characters and + # "/" or "%" characters. + # my_external_metric_name_example: + # + # NRQL query that will executed to obtain the metric value. + # The query must return just one value so is recommended to use aggregator functions like average or latest. + # Default time span for aggregator func is 1h so is recommended to use the SINCE clause to reduce the time span. + # query: "FROM Metric SELECT average(`k8s.container.cpuCoresUtilization`) SINCE 2 MINUTES AGO" + # + # By default a cluster filter is added to the query to ensure no cross cluster metrics are taking into account. + # The added filter is equivalent to WHERE `clusterName`=. + # If metrics are not from the cluster use removeClusterFilter. Default value for this parameter is false. + # removeClusterFilter: false + + # config.nrdbClientTimeoutSeconds -- Defines the NRDB client timeout. The maximum allowed value is 120. + # @default -- 30 + nrdbClientTimeoutSeconds: 30 + +# image -- Registry, repository, tag, and pull policy for the container image. +# @default -- See `values.yaml`. +image: + registry: + repository: newrelic/newrelic-k8s-metrics-adapter + tag: "" + pullPolicy: IfNotPresent + # It is possible to specify docker registry credentials. + # See https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod + # image.pullSecrets -- The image pull secrets. + pullSecrets: [] + # - name: regsecret + +# -- Number of replicas in the deployment. +replicas: 1 + +# -- Resources you wish to assign to the pod. +# @default -- See `values.yaml` +resources: + limits: + memory: 80M + requests: + cpu: 100m + memory: 30M + +serviceAccount: + # -- Specifies whether a ServiceAccount should be created for the job and the deployment. + # false avoids creation, true or empty will create the ServiceAccount + # @default -- `true` + create: + # -- If `serviceAccount.create` this will be the name of the ServiceAccount to use. + # If not set and create is true, a name is generated using the fullname template. + # If create is false, a serviceAccount with the given name must exist + # @default -- Automatically generated. + name: + +# -- Configure podSecurityContext +podSecurityContext: + +# -- Configure containerSecurityContext +containerSecurityContext: + +# -- Array to add extra environment variables +extraEnv: [] +# -- Array to add extra envFrom +extraEnvFrom: [] +# -- Array to add extra volumes +extraVolumes: [] +# -- Add extra volume mounts +extraVolumeMounts: [] + +# -- Additional annotations to apply to the pod(s). +podAnnotations: + +# Due to security restrictions, some users might require to use a https proxy to route traffic over the internet. +# In this specific case, when the metrics adapter sends a request to the New Relic backend. If this is the case +# for you, set this value to your http proxy endpoint. +# -- Configure proxy for the metrics-adapter. +proxy: + +# Pod scheduling priority +# Ref: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/ +# priorityClassName: high-priority + +# fullnameOverride -- To fully override common.naming.fullname +fullnameOverride: "" +# -- Node affinity to use for scheduling. +affinity: {} +# -- Node label to use for scheduling. +nodeSelector: {} +# -- List of node taints to tolerate (requires Kubernetes >= 1.6) +tolerations: [] + +apiServicePatchJob: + # apiServicePatchJob.image -- Registry, repository, tag, and pull policy for the job container image. + # @default -- See `values.yaml`. + image: + registry: # defaults to registry.k8s.io + repository: ingress-nginx/kube-webhook-certgen + tag: v1.3.0 + pullPolicy: IfNotPresent + + # -- Additional Volumes for Cert Job. + volumes: [] + # - name: tmp + # emptyDir: {} + + # -- Additional Volume mounts for Cert Job, you might want to mount tmp if Pod Security Policies. + volumeMounts: [] + # - name: tmp + # mountPath: /tmp + # Enforce a read-only root. + +certManager: + # -- Use cert manager for APIService certs, rather than the built-in patch job. + enabled: false + +rbac: + # rbac.pspEnabled -- Whether the chart should create Pod Security Policy objects. + pspEnabled: false diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/Chart.lock b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/Chart.lock new file mode 100644 index 0000000000..064abf8aae --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: common-library + repository: https://helm-charts.newrelic.com + version: 1.2.0 +digest: sha256:fa87cb007564a39a72739a3e850a91d6b03c0fc27a1115deac042b3ef77b4142 +generated: "2024-07-17T19:29:15.951407+05:30" diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/Chart.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/Chart.yaml new file mode 100644 index 0000000000..26f48a8a16 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/Chart.yaml @@ -0,0 +1,20 @@ +apiVersion: v2 +appVersion: 2.0.0 +dependencies: +- name: common-library + repository: https://helm-charts.newrelic.com + version: 1.2.0 +description: A Helm chart to deploy New Relic Kubernetes Logging as a DaemonSet, supporting + both Linux and Windows nodes and containers +home: https://github.com/newrelic/kubernetes-logging +icon: https://newrelic.com/assets/newrelic/source/NewRelic-logo-square.svg +keywords: +- logging +- newrelic +maintainers: +- email: logging-team@newrelic.com + name: jsubirat +- name: danybmx +- name: sdaubin +name: newrelic-logging +version: 1.22.4 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/README.md b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/README.md new file mode 100644 index 0000000000..1635b0d863 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/README.md @@ -0,0 +1,268 @@ +# newrelic-logging + + +## Chart Details +New Relic offers a [Fluent Bit](https://fluentbit.io/) output [plugin](https://github.com/newrelic/newrelic-fluent-bit-output) to easily forward your logs to [New Relic Logs](https://docs.newrelic.com/docs/logs/new-relic-logs/get-started/introduction-new-relic-logs). This plugin is also provided in a standalone Docker image that can be installed in a [Kubernetes](https://kubernetes.io/) cluster in the form of a [DaemonSet](https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/), which we refer as the Kubernetes plugin. + +This document explains how to install it in your cluster using our [Helm](https://helm.sh/) chart. + + +## Install / Upgrade / Uninstall instructions +Despite the `newrelic-logging` chart being able to work standalone, we recommend installing it as part of the [`nri-bundle`](https://github.com/newrelic/helm-charts/tree/master/charts/nri-bundle) chart. The best way of doing so is through the guided installation process documented [here](https://docs.newrelic.com//docs/kubernetes-pixie/kubernetes-integration/installation/kubernetes-integration-install-configure/). This guided install can generate the Helm 3 commands required to install it (select "Helm 3" in Step 3 from the previous documentation link). You can also opt to install it manually using Helm by following [these steps](https://docs.newrelic.com//docs/kubernetes-pixie/kubernetes-integration/installation/install-kubernetes-integration-using-helm/#install-k8-helm). To uninstall it, refer to the steps outlined in [this page](https://docs.newrelic.com/docs/kubernetes-pixie/kubernetes-integration/uninstall-kubernetes/). + +### Installing or updating the helm New Relic repository + +To install the repo you can run: +``` +helm repo add newrelic https://helm-charts.newrelic.com +``` + +To update the repo you can run: +``` +helm repo update newrelic +``` + +## Configuration + +### How to configure the chart +The `newrelic-logging` chart can be installed either alone or as part of the [`nri-bundle`](https://github.com/newrelic/helm-charts/tree/master/charts/nri-bundle) chart (recommended). The chart default settings should be suitable for most users. Nevertheless, you may be interested in overriding the defaults, either by passing them through a `values-newrelic.yaml` file or via the command line when installing the chart. Depending on how you installed it, you'll need to specify the `newrelic-logging`-specific configuration values using the chart name (`newrelic-logging`) as a prefix. In the table below, you can find a quick reference of how to configure the chart in these scenarios. The example depicts how you'd specify the mandatory `licenseKey` and `cluster` settings and how you'd override the `fluentBit.retryLimit` setting to `10`. + + + + + + + + + + + + + + + + + +
    Installation methodConfiguration via values.yamlConfiguration via command line
    Standalone newrelic-logging + + +``` +# values-newrelic.yaml configuration contents + +licenseKey: _YOUR_NEW_RELIC_LICENSE_KEY_ +cluster: _K8S_CLUSTER_NAME_ + +fluentBit: + retryLimit: 10 +``` + +``` +# Install / upgrade command + +helm upgrade --install newrelic-logging newrelic/newrelic-logging \ +--namespace newrelic \ +--create-namespace \ +-f values-newrelic.yaml +``` + + +``` +# Install / upgrade command + +helm upgrade --install newrelic-logging newrelic/newrelic-logging \ +--namespace=newrelic \ +--set licenseKey=_YOUR_NEW_RELIC_LICENSE_KEY_ \ +--set cluster=_K8S_CLUSTER_NAME_ \ +--set fluentBit.retryLimit=10 +``` +
    As part of nri-bundle + +``` +# values-newrelic.yaml configuration contents + +# General settings that apply to all the child charts +global: + licenseKey: _YOUR_NEW_RELIC_LICENSE_KEY_ + cluster: _K8S_CLUSTER_NAME_ + +# Specific configuration for the newrelic-logging child chart +newrelic-logging: + fluentBit: + retryLimit: 10 +``` + +``` +# Install / upgrade command + +helm upgrade --install newrelic-bundle newrelic/nri-bundle \ + --namespace newrelic \ + --create-namespace \ + -f values-newrelic.yaml \ +``` + + +``` +# Install / upgrade command + +helm upgrade --install newrelic-bundle newrelic/nri-bundle \ +--namespace=newrelic \ +--set global.licenseKey=_YOUR_NEW_RELIC_LICENSE_KEY_ \ +--set global.cluster=_K8S_CLUSTER_NAME_ \ +--set newrelic-logging.fluentBit.retryLimit=10 +``` +
    + + +### Supported configuration parameters +See [values.yaml](values.yaml) for the default values + +| Parameter | Description | Default | +| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------- | +| `global.cluster` - `cluster` | The cluster name for the Kubernetes cluster. | | +| `global.licenseKey` - `licenseKey` | The [license key](https://docs.newrelic.com/docs/accounts/install-new-relic/account-setup/license-key) for your New Relic Account. This will be the preferred configuration option if both `licenseKey` and `customSecret*` values are specified. | | +| `global.customSecretName` - `customSecretName` | Name of the Secret object where the license key is stored | | +| `global.customSecretLicenseKey` - `customSecretLicenseKey` | Key in the Secret object where the license key is stored. | | +| `global.fargate` | Must be set to `true` when deploying in an EKS Fargate environment. Prevents DaemonSet pods from being scheduled in Fargate nodes. | | +| `global.lowDataMode` - `lowDataMode` | If `true`, send minimal attributes on Kubernetes logs. Labels and annotations are not sent when lowDataMode is enabled. | `false` | +| `rbac.create` | Enable Role-based authentication | `true` | +| `rbac.pspEnabled` | Enable pod security policy support | `false` | +| `image.repository` | The container to pull. | `newrelic/newrelic-fluentbit-output` | +| `image.pullPolicy` | The pull policy. | `IfNotPresent` | +| `image.pullSecrets` | Image pull secrets. | `nil` | +| `image.tag` | The version of the container to pull. | See value in [values.yaml]` | +| `exposedPorts` | Any ports you wish to expose from the pod. Ex. 2020 for metrics | `[]` | +| `resources` | Any resources you wish to assign to the pod. | See Resources below | +| `priorityClassName` | Scheduling priority of the pod | `nil` | +| `nodeSelector` | Node label to use for scheduling on Linux nodes | `{ kubernetes.io/os: linux }` | +| `windowsNodeSelector` | Node label to use for scheduling on Windows nodes | `{ kubernetes.io/os: windows, node.kubernetes.io/windows-build: BUILD_NUMBER }` | +| `tolerations` | List of node taints to tolerate (requires Kubernetes >= 1.6) | See Tolerations below | +| `updateStrategy` | Strategy for DaemonSet updates (requires Kubernetes >= 1.6) | `RollingUpdate` | +| `extraVolumeMounts` | Additional DaemonSet volume mounts | `[]` | +| `extraVolumes` | Additional DaemonSet volumes | `[]` | +| `initContainers` | [Init containers](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/) that will be executed before the actual container in charge of shipping logs to New Relic is initialized. Use this if you are using a custom Fluent Bit configuration that requires downloading certain files inside the volumes being accessed by the log-shipping pod. | `[]` | +| `windows.initContainers` | [Init containers](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/) that will be executed before the actual container in charge of shipping logs to New Relic is initialized. Use this if you are using a custom Fluent Bit configuration that requires downloading certain files inside the volumes being accessed by the log-shipping pod. | `[]` | +| `serviceAccount.create` | If true, a service account would be created and assigned to the deployment | `true` | +| `serviceAccount.name` | The service account to assign to the deployment. If `serviceAccount.create` is true then this name will be used when creating the service account | | +| `serviceAccount.annotations` | The annotations to add to the service account if `serviceAccount.create` is set to true. | | +| `global.nrStaging` - `nrStaging` | Send data to staging (requires a staging license key) | `false` | +| `fluentBit.path` | Node path logs are forwarded from. Patterns are supported, as well as specifying multiple paths/patterns separated by commas. | `/var/log/containers/*.log` | +| `fluentBit.linuxMountPath` | The path mounted on linux Fluent-Bit pods to read logs from. Defaults to /var because some engines write the logs to /var/log and others to /var/lib (symlinked to /var/log) so Fluent-Bit need access to both in those cases | `/var` | +| `fluentBit.db` | Node path used by Fluent Bit to store a database file to keep track of monitored files and offsets. | `/var/log/containers/*.log` | +| `fluentBit.k8sBufferSize` | Set the buffer size for HTTP client when reading responses from Kubernetes API server. A value of 0 results in no limit and the buffer will expand as needed. | `32k` | +| `fluentBit.k8sLoggingExclude` | Set to "true" to allow excluding pods by adding the annotation `fluentbit.io/exclude: "true"` to pods you wish to exclude. | `false` | +| `fluentBit.additionalEnvVariables` | Additional environmental variables for fluentbit pods | `[]]` | +| `fluentBit.persistence.mode` | The [persistence mode](#Fluent-Bit-persistence-modes) you want to use, options are "hostPath", "none" or "persistentVolume" (this last one available only for linux) | +| `fluentBit.persistence.persistentVolume.storageClass` | On "persistentVolume" [persistence mode](#Fluent-Bit-persistence-modes), indicates the storage class that will be used for create the PersistentVolume and PersistentVolumeClaim. | | +| `fluentBit.persistence.persistentVolume.size` | On "persistentVolume" [persistence mode](#Fluent-Bit-persistence-modes), indicates the capacity for the PersistentVolume and PersistentVolumeClaim | 10Gi | +| `fluentBit.persistence.persistentVolume.dynamicProvisioning` | On "persistentVolume" [persistence mode](#Fluent-Bit-persistence-modes), indicates if the storage class used provide dynamic provisioning. If it does, only the PersistentVolumeClaim will be created. | true | +| `fluentBit.persistence.persistentVolume.existingVolume` | On "persistentVolume" [persistence mode](#Fluent-Bit-persistence-modes), indicates and existing volume in case you want to reuse one, bear in mind that it should allow ReadWriteMany access mode. A PersistentVolumeClaim will be created using it. | | +| `fluentBit.persistence.persistentVolume.existingVolumeClaim` | On "persistentVolume" [persistence mode](#Fluent-Bit-persistence-modes), indicates and existing volume claim that will be used on the daemonset. It should allow ReadWriteMany access mode. | | +| `fluentBit.persistence.persistentVolume.annotations.volume` | On "persistentVolume" [persistence mode](#Fluent-Bit-persistence-modes), allows to add annotations to the PersistentVolume (if created). | | +| `fluentBit.persistence.persistentVolume.annotations.claim` | On "persistentVolume" [persistence mode](#Fluent-Bit-persistence-modes), allows to add annotations to the PersistentVolumeClaim (if created). | | +| `fluentBit.persistence.persistentVolume.extra.volume` | On "persistentVolume" [persistence mode](#Fluent-Bit-persistence-modes), allows to add extra properties to the PersistentVolume (if created). | | +| `fluentBit.persistence.persistentVolume.extra.claim` | On "persistentVolume" [persistence mode](#Fluent-Bit-persistence-modes), allows to add extra properties to the PersistentVolumeClaim (if created). | | +| `daemonSet.annotations` | The annotations to add to the `DaemonSet`. | | +| `podAnnotations` | The annotations to add to the `DaemonSet` created `Pod`s. | | +| `hostNetwork` | Set the hostNetwork property for fluentbit pods. | | +| `enableLinux` | Enable log collection from Linux containers. This is the default behavior. In case you are only interested of collecting logs from Windows containers, set this to `false`. | `true` | +| `enableWindows` | Enable log collection from Windows containers. Please refer to the [Windows support](#windows-support) section for more details. | `false` | +| `fluentBit.config.service` | Contains fluent-bit.conf Service config | | +| `fluentBit.config.inputs` | Contains fluent-bit.conf Inputs config | | +| `fluentBit.config.extraInputs` | Contains extra fluent-bit.conf Inputs config | | +| `fluentBit.config.filters` | Contains fluent-bit.conf Filters config | | +| `fluentBit.config.extraFilters` | Contains extra fluent-bit.conf Filters config | | +| `fluentBit.config.lowDataModeFilters` | Contains fluent-bit.conf Filters config for lowDataMode | | +| `fluentBit.config.outputs` | Contains fluent-bit.conf Outputs config | | +| `fluentBit.config.extraOutputs` | Contains extra fluent-bit.conf Outputs config | | +| `fluentBit.config.parsers` | Contains parsers.conf Parsers config | | +| `fluentBit.retryLimit` | Amount of times to retry sending a given batch of logs to New Relic. This prevents data loss if there is a temporary network disruption, if a request to the Logs API is lost or when receiving a recoverable HTTP response. Set it to "False" for unlimited retries. | 5 | +| `fluentBit.sendMetrics` | Enable the collection of Fluent Bit internal metrics in Prometheus format as well as newrelic-fluent-bit-output internal plugin metrics. See [this documentation page](https://docs.newrelic.com/docs/logs/forward-logs/kubernetes-plugin-log-forwarding/#troubleshoot-installation) for more details. | `false` | +| `dnsConfig` | [DNS configuration](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-dns-config) that will be added to the pods. Can be configured also with `global.dnsConfig`. | `{}` | +| `fluentBit.criEnabled` | We assume that `kubelet`directly communicates with the container engine using the [CRI](https://kubernetes.io/docs/concepts/overview/components/#container-runtime) specification. Set this to `false` if your K8s installation uses [dockershim](https://kubernetes.io/docs/tasks/administer-cluster/migrating-from-dockershim/) instead, in order to get the logs properly parsed. | `true` | + +### Fluent Bit persistence modes + +Fluent Bit uses a database file to keep track of log lines read from files (offsets). This database file is stored in the host node by default, using a `hostPath` mount. It's specifically stored (by default) in `/var/log/flb_kube.db` to keep things simple, as we're already mounting `/var` for accessing container logs. + +Sometimes the security constraints of some clusters don't allow mounting `hostPath`s in read-write mode. That's why you can chose among the following +persistence modes. Each one has their pros and cons. + +- `hostPath` (default) will use a `hostPath` mount to store the DB file on the node disk. This is the easiest, cheapest an most reliable option, but prohibited by some cloud vendor security policies. +- `none` will disable the Fluent Bit DB file. This can cause log duplication or data loss in case Fluent Bit gets restarted. +- `persistentVolume` (Linux only) will use a `ReadWriteMany` persistent volume to store the DB file. This will override the `fluentBit.db` path and use `/db/${NODE_NAME}-fb.db` instead. If you use this option in a Windows cluster it will default to `none` on Windows nodes. + +#### GKE Autopilot example + +If you're using the `persistentVolume` persistence mode you need to provide at least the `storageClass`, and it should be `ReadWriteMany`. This is an example of the configuration for persistence in [GKE Autopilot](https://cloud.google.com/kubernetes-engine/docs/concepts/autopilot-overview). + +``` +fluentBit: + persistence: + mode: persistentVolume + persistentVolume: + storageClass: standard-rwx + linuxMountPath: /var/log +``` + +### Proxy support + +Since Fluent Bit Kubernetes plugin is using [newrelic-fluent-bit-output](https://github.com/newrelic/newrelic-fluent-bit-output) we can configure the [proxy support](https://github.com/newrelic/newrelic-fluent-bit-output#proxy-support) in order to set up the proxy configuration. + +#### As environment variables + +The easiest way to configure the proxy is by means of specifying the `HTTP_PROXY` or `HTTPS_PROXY` variables as follows: + +``` +# values-newrelic.yml + +fluentBit: + additionalEnvVariables: + - name: HTTPS_PROXY + value: https://your-https-proxy-hostname:3129 +``` + + +#### Custom proxy configuration (for proxies using self-signed certificates) + +If you need to use a proxy using self-signed certificates, you'll need to mount a volume with the Certificate Authority +bundle file and reference it from the Fluent Bit configuration as follows: + +``` +# values-newrelic.yaml +extraVolumes: [] + - name: proxyConfig + # Example using hostPath. You can also place the caBundleFile.pem contents in a ConfigMap and reference it here instead, + # as explained here: https://kubernetes.io/docs/concepts/storage/volumes/#configmap + hostPath: + path: /path/in/node/to/your/caBundleFile.pem + +extraVolumeMounts: [] + - name: proxyConfig + mountPath: /proxyConfig/caBundleFile.pem + +fluentBit: + config: + outputs: | + [OUTPUT] + Name newrelic + Match * + licenseKey ${LICENSE_KEY} + endpoint ${ENDPOINT} + lowDataMode ${LOW_DATA_MODE} + Retry_Limit ${RETRY_LIMIT} + proxy https://your-https-proxy-hostname:3129 + caBundleFile /proxyConfig/caBundleFile.pem +``` + + +## Windows support + +Since version `1.7.0`, this Helm chart supports shipping logs from Windows containers. To this end, you need to set the `enableWindows` configuration parameter to `true`. + +Windows containers have some constraints regarding Linux containers. The main one being that they can only be executed on _hosts_ using the exact same Windows version and build number. On the other hand, Kubernetes nodes only supports the Windows versions listed [here](https://kubernetes.io/docs/setup/production-environment/windows/intro-windows-in-kubernetes/#windows-os-version-support). + +This Helm chart deploys one `DaemonSet` for each of the Windows versions it supports, while ensuring that only containers matching the host operating system will be deployed in each host. + +This Helm chart currently supports the following Windows versions: +- Windows Server LTSC 2019, build 10.0.17763 +- Windows Server LTSC 2022, build 10.0.20348 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/.helmignore b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/.helmignore new file mode 100644 index 0000000000..0e8a0eb36f --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/.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 +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/Chart.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/Chart.yaml new file mode 100644 index 0000000000..b65ac15d4c --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/Chart.yaml @@ -0,0 +1,17 @@ +apiVersion: v2 +description: Provides helpers to provide consistency on all the charts +keywords: +- newrelic +- chart-library +maintainers: +- name: juanjjaramillo + url: https://github.com/juanjjaramillo +- name: csongnr + url: https://github.com/csongnr +- name: dbudziwojskiNR + url: https://github.com/dbudziwojskiNR +- name: kang-makes + url: https://github.com/kang-makes +name: common-library +type: library +version: 1.2.0 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/DEVELOPERS.md b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/DEVELOPERS.md new file mode 100644 index 0000000000..3ccc108e2d --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/DEVELOPERS.md @@ -0,0 +1,663 @@ +# Functions/templates documented for chart writers +Here is some rough documentation separated by the file that contains the function, the function +name and how to use it. We are not covering functions that start with `_` (e.g. +`newrelic.common.license._licenseKey`) because they are used internally by this library for +other helpers. Helm does not have the concept of "public" or "private" functions/templates so +this is a convention of ours. + +## _naming.tpl +These functions are used to name objects. + +### `newrelic.common.naming.name` +This is the same as the idiomatic `CHART-NAME.name` that is created when you use `helm create`. + +It honors `.Values.nameOverride`. + +Usage: +```mustache +{{ include "newrelic.common.naming.name" . }} +``` + +### `newrelic.common.naming.fullname` +This is the same as the idiomatic `CHART-NAME.fullname` that is created when you use `helm create` + +It honors `.Values.fullnameOverride`. + +Usage: +```mustache +{{ include "newrelic.common.naming.fullname" . }} +``` + +### `newrelic.common.naming.chart` +This is the same as the idiomatic `CHART-NAME.chart` that is created when you use `helm create`. + +It is mostly useless for chart writers. It is used internally for templating the labels but there +is no reason to keep it "private". + +Usage: +```mustache +{{ include "newrelic.common.naming.chart" . }} +``` + +### `newrelic.common.naming.truncateToDNS` +This is a useful template that could be used to trim a string to 63 chars and does not end with a dash (`-`). +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). + +Usage: +```mustache +{{ $nameToTruncate := "a-really-really-really-really-REALLY-long-string-that-should-be-truncated-because-it-is-enought-long-to-brak-something" +{{- $truncatedName := include "newrelic.common.naming.truncateToDNS" $nameToTruncate }} +{{- $truncatedName }} +{{- /* This should print: a-really-really-really-really-REALLY-long-string-that-should-be */ -}} +``` + +### `newrelic.common.naming.truncateToDNSWithSuffix` +This template function is the same as the above but instead of receiving a string you should give a `dict` +with a `name` and a `suffix`. This function will join them with a dash (`-`) and trim the `name` so the +result of `name-suffix` is no more than 63 chars + +Usage: +```mustache +{{ $nameToTruncate := "a-really-really-really-really-REALLY-long-string-that-should-be-truncated-because-it-is-enought-long-to-brak-something" +{{- $suffix := "A-NOT-SO-LONG-SUFFIX" }} +{{- $truncatedName := include "truncateToDNSWithSuffix" (dict "name" $nameToTruncate "suffix" $suffix) }} +{{- $truncatedName }} +{{- /* This should print: a-really-really-really-really-REALLY-long-A-NOT-SO-LONG-SUFFIX */ -}} +``` + + + +## _labels.tpl +### `newrelic.common.labels`, `newrelic.common.labels.selectorLabels` and `newrelic.common.labels.podLabels` +These are functions that are used to label objects. They are configured by this `values.yaml` +```yaml +global: + podLabels: {} # included in all the pods of all the charts that implement this library + labels: {} # included in all the objects of all the charts that implement this library +podLabels: {} # included in all the pods of this chart +labels: {} # included in all the objects of this chart +``` + +label maps are merged from global to local values. + +And chart writer should use them like this: +```mustache +metadata: + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +spec: + selector: + matchLabels: + {{- include "newrelic.common.labels.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "newrelic.common.labels.podLabels" . | nindent 8 }} +``` + +`newrelic.common.labels.podLabels` includes `newrelic.common.labels.selectorLabels` automatically. + + + +## _priority-class-name.tpl +### `newrelic.common.priorityClassName` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + priorityClassName: "" +priorityClassName: "" +``` + +Be careful: chart writers should put an empty string (or any kind of Helm falsiness) for this +library to work properly. If in your values a non-falsy `priorityClassName` is found, the global +one is going to be always ignored. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.priorityClassName" . }} + priorityClassName: {{ . }} + {{- end }} +``` + + + +## _hostnetwork.tpl +### `newrelic.common.hostNetwork` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + hostNetwork: # Note that this is empty (nil) +hostNetwork: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `hostNetwork` is defined, the global one is going to be always ignored. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.hostNetwork" . }} + hostNetwork: {{ . }} + {{- end }} +``` + +### `newrelic.common.hostNetwork.value` +This function is an abstraction of the function above but this returns directly "true" or "false". + +Be careful with using this with an `if` as Helm does evaluate "false" (string) as `true`. + +Usage (example in a pod spec): +```mustache +spec: + hostNetwork: {{ include "newrelic.common.hostNetwork.value" . }} +``` + + + +## _dnsconfig.tpl +### `newrelic.common.dnsConfig` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + dnsConfig: {} +dnsConfig: {} +``` + +Be careful: chart writers should put an empty string (or any kind of Helm falsiness) for this +library to work properly. If in your values a non-falsy `dnsConfig` is found, the global +one is going to be always ignored. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.dnsConfig" . }} + dnsConfig: + {{- . | nindent 4 }} + {{- end }} +``` + + + +## _images.tpl +These functions help us to deal with how images are templated. This allows setting `registries` +where to fetch images globally while being flexible enough to fit in different maps of images +and deployments with one or more images. This is the example of a complex `values.yaml` that +we are going to use during the documentation of these functions: + +```yaml +global: + images: + registry: nexus-3-instance.internal.clients-domain.tld +jobImage: + registry: # defaults to "example.tld" when empty in these examples + repository: ingress-nginx/kube-webhook-certgen + tag: v1.1.1 + pullPolicy: IfNotPresent + pullSecrets: [] +images: + integration: + registry: + repository: newrelic/nri-kube-events + tag: 1.8.0 + pullPolicy: IfNotPresent + agent: + registry: + repository: newrelic/k8s-events-forwarder + tag: 1.22.0 + pullPolicy: IfNotPresent + pullSecrets: [] +``` + +### `newrelic.common.images.image` +This will return a string with the image ready to be downloaded that includes the registry, the image and the tag. +`defaultRegistry` is used to keep `registry` field empty in `values.yaml` so you can override the image using +`global.images.registry`, your local `jobImage.registry` and be able to fallback to a registry that is not `docker.io` +(Or the default repository that the client could have set in the CRI). + +Usage: +```mustache +{{- /* For the integration */}} +{{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.images.agent "context" .) }} +{{- /* For jobImage */}} +{{ include "newrelic.common.images.image" ( dict "defaultRegistry" "example.tld" "imageRoot" .Values.jobImage "context" .) }} +``` + +### `newrelic.common.images.registry` +It returns the registry from the global or local values. You should avoid using this helper to create your image +URL and use `newrelic.common.images.image` instead, but it is there to be used in case it is needed. + +Usage: +```mustache +{{- /* For the integration */}} +{{ include "newrelic.common.images.registry" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.registry" ( dict "imageRoot" .Values.images.agent "context" .) }} +{{- /* For jobImage */}} +{{ include "newrelic.common.images.registry" ( dict "defaultRegistry" "example.tld" "imageRoot" .Values.jobImage "context" .) }} +``` + +### `newrelic.common.images.repository` +It returns the image from the values. You should avoid using this helper to create your image +URL and use `newrelic.common.images.image` instead, but it is there to be used in case it is needed. + +Usage: +```mustache +{{- /* For jobImage */}} +{{ include "newrelic.common.images.repository" ( dict "imageRoot" .Values.jobImage "context" .) }} +{{- /* For the integration */}} +{{ include "newrelic.common.images.repository" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.repository" ( dict "imageRoot" .Values.images.agent "context" .) }} +``` + +### `newrelic.common.images.tag` +It returns the image's tag from the values. You should avoid using this helper to create your image +URL and use `newrelic.common.images.image` instead, but it is there to be used in case it is needed. + +Usage: +```mustache +{{- /* For jobImage */}} +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.jobImage "context" .) }} +{{- /* For the integration */}} +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.images.agent "context" .) }} +``` + +### `newrelic.common.images.renderPullSecrets` +If returns a merged map that contains the pull secrets from the global configuration and the local one. + +Usage: +```mustache +{{- /* For jobImage */}} +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" .Values.jobImage.pullSecrets "context" .) }} +{{- /* For the integration */}} +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" .Values.images.pullSecrets "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" .Values.images.pullSecrets "context" .) }} +``` + + + +## _serviceaccount.tpl +These functions are used to evaluate if the service account should be created, with which name and add annotations to it. + +The functions that the common library has implemented for service accounts are: +* `newrelic.common.serviceAccount.create` +* `newrelic.common.serviceAccount.name` +* `newrelic.common.serviceAccount.annotations` + +Usage: +```mustache +{{- if include "newrelic.common.serviceAccount.create" . -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + {{- with (include "newrelic.common.serviceAccount.annotations" .) }} + annotations: + {{- . | nindent 4 }} + {{- end }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "newrelic.common.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} +{{- end }} +``` + + + +## _affinity.tpl, _nodeselector.tpl and _tolerations.tpl +These three files are almost the same and they follow the idiomatic way of `helm create`. + +Each function also looks if there is a global value like the other helpers. +```yaml +global: + affinity: {} + nodeSelector: {} + tolerations: [] +affinity: {} +nodeSelector: {} +tolerations: [] +``` + +The values here are replaced instead of be merged. If a value at root level is found, the global one is ignored. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.nodeSelector" . }} + nodeSelector: + {{- . | nindent 4 }} + {{- end }} + {{- with include "newrelic.common.affinity" . }} + affinity: + {{- . | nindent 4 }} + {{- end }} + {{- with include "newrelic.common.tolerations" . }} + tolerations: + {{- . | nindent 4 }} + {{- end }} +``` + + + +## _agent-config.tpl +### `newrelic.common.agentConfig.defaults` +This returns a YAML that the agent can use directly as a config that includes other options from the values file like verbose mode, +custom attributes, FedRAMP and such. + +Usage: +```mustache +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include newrelic.common.naming.truncateToDNSWithSuffix (dict "name" (include "newrelic.common.naming.fullname" .) suffix "agent-config") }} + namespace: {{ .Release.Namespace }} +data: + newrelic-infra.yml: |- + # This is the configuration file for the infrastructure agent. See: + # https://docs.newrelic.com/docs/infrastructure/install-infrastructure-agent/configuration/infrastructure-agent-configuration-settings/ + {{- include "newrelic.common.agentConfig.defaults" . | nindent 4 }} +``` + + + +## _cluster.tpl +### `newrelic.common.cluster` +Returns the cluster name + +Usage: +```mustache +{{ include "newrelic.common.cluster" . }} +``` + + + +## _custom-attributes.tpl +### `newrelic.common.customAttributes` +Return custom attributes in YAML format. + +Usage: +```mustache +apiVersion: v1 +kind: ConfigMap +metadata: + name: example +data: + custom-attributes.yaml: | + {{- include "newrelic.common.customAttributes" . | nindent 4 }} + custom-attributes.json: | + {{- include "newrelic.common.customAttributes" . | fromYaml | toJson | nindent 4 }} +``` + + + +## _fedramp.tpl +### `newrelic.common.fedramp.enabled` +Returns true if FedRAMP is enabled or an empty string if not. It can be safely used in conditionals as an empty string is a Helm falsiness. + +Usage: +```mustache +{{ include "newrelic.common.fedramp.enabled" . }} +``` + +### `newrelic.common.fedramp.enabled.value` +Returns true if FedRAMP is enabled or false if not. This is to have the value of FedRAMP ready to be templated. + +Usage: +```mustache +{{ include "newrelic.common.fedramp.enabled.value" . }} +``` + + + +## _license.tpl +### `newrelic.common.license.secretName` and ### `newrelic.common.license.secretKeyName` +Returns the secret and key inside the secret where to read the license key. + +The common library will take care of using a user-provided custom secret or creating a secret that contains the license key. + +To create the secret use `newrelic.common.license.secret`. + +Usage: +```mustache +{{- if and (.Values.controlPlane.enabled) (not (include "newrelic.fargate" .)) }} +apiVersion: v1 +kind: Pod +metadata: + name: example +spec: + containers: + - name: agent + env: + - name: "NRIA_LICENSE_KEY" + valueFrom: + secretKeyRef: + name: {{ include "newrelic.common.license.secretName" . }} + key: {{ include "newrelic.common.license.secretKeyName" . }} +``` + + + +## _license_secret.tpl +### `newrelic.common.license.secret` +This function templates the secret that is used by agents and integrations with the license Key provided by the user. It will +template nothing (empty string) if the user provides a custom pair of secret name and key. + +This template also fails in case the user has not provided any license key or custom secret so no safety checks have to be done +by chart writers. + +You just must have a template with these two lines: +```mustache +{{- /* Common library will take care of creating the secret or not. */ -}} +{{- include "newrelic.common.license.secret" . -}} +``` + + + +## _insights.tpl +### `newrelic.common.insightsKey.secretName` and ### `newrelic.common.insightsKey.secretKeyName` +Returns the secret and key inside the secret where to read the insights key. + +The common library will take care of using a user-provided custom secret or creating a secret that contains the insights key. + +To create the secret use `newrelic.common.insightsKey.secret`. + +Usage: +```mustache +apiVersion: v1 +kind: Pod +metadata: + name: statsd +spec: + containers: + - name: statsd + env: + - name: "INSIGHTS_KEY" + valueFrom: + secretKeyRef: + name: {{ include "newrelic.common.insightsKey.secretName" . }} + key: {{ include "newrelic.common.insightsKey.secretKeyName" . }} +``` + + + +## _insights_secret.tpl +### `newrelic.common.insightsKey.secret` +This function templates the secret that is used by agents and integrations with the insights key provided by the user. It will +template nothing (empty string) if the user provides a custom pair of secret name and key. + +This template also fails in case the user has not provided any insights key or custom secret so no safety checks have to be done +by chart writers. + +You just must have a template with these two lines: +```mustache +{{- /* Common library will take care of creating the secret or not. */ -}} +{{- include "newrelic.common.insightsKey.secret" . -}} +``` + + + +## _low-data-mode.tpl +### `newrelic.common.lowDataMode` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + lowDataMode: # Note that this is empty (nil) +lowDataMode: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `lowdataMode` is defined, the global one is going to be always ignored. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage: +```mustache +{{ include "newrelic.common.lowDataMode" . }} +``` + + + +## _privileged.tpl +### `newrelic.common.privileged` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + privileged: # Note that this is empty (nil) +privileged: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `privileged` is defined, the global one is going to be always ignored. + +Chart writers could override this and put directly a `true` in the `values.yaml` to override the +default of the common library. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage: +```mustache +{{ include "newrelic.common.privileged" . }} +``` + +### `newrelic.common.privileged.value` +Returns true if privileged mode is enabled or false if not. This is to have the value of privileged ready to be templated. + +Usage: +```mustache +{{ include "newrelic.common.privileged.value" . }} +``` + + + +## _proxy.tpl +### `newrelic.common.proxy` +Returns the proxy URL configured by the user. + +Usage: +```mustache +{{ include "newrelic.common.proxy" . }} +``` + + + +## _security-context.tpl +Use these functions to share the security context among all charts. Useful in clusters that have security enforcing not to +use the root user (like OpenShift) or users that have an admission webhooks. + +The functions are: +* `newrelic.common.securityContext.container` +* `newrelic.common.securityContext.pod` + +Usage: +```mustache +apiVersion: v1 +kind: Pod +metadata: + name: example +spec: + spec: + {{- with include "newrelic.common.securityContext.pod" . }} + securityContext: + {{- . | nindent 8 }} + {{- end }} + + containers: + - name: example + {{- with include "nriKubernetes.securityContext.container" . }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} +``` + + + +## _staging.tpl +### `newrelic.common.nrStaging` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + nrStaging: # Note that this is empty (nil) +nrStaging: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `nrStaging` is defined, the global one is going to be always ignored. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage: +```mustache +{{ include "newrelic.common.nrStaging" . }} +``` + +### `newrelic.common.nrStaging.value` +Returns true if staging is enabled or false if not. This is to have the staging value ready to be templated. + +Usage: +```mustache +{{ include "newrelic.common.nrStaging.value" . }} +``` + + + +## _verbose-log.tpl +### `newrelic.common.verboseLog` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + verboseLog: # Note that this is empty (nil) +verboseLog: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `verboseLog` is defined, the global one is going to be always ignored. + +Usage: +```mustache +{{ include "newrelic.common.verboseLog" . }} +``` + +### `newrelic.common.verboseLog.valueAsBoolean` +Returns true if verbose is enabled or false if not. This is to have the verbose value ready to be templated as a boolean + +Usage: +```mustache +{{ include "newrelic.common.verboseLog.valueAsBoolean" . }} +``` + +### `newrelic.common.verboseLog.valueAsInt` +Returns 1 if verbose is enabled or 0 if not. This is to have the verbose value ready to be templated as an integer + +Usage: +```mustache +{{ include "newrelic.common.verboseLog.valueAsInt" . }} +``` diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/README.md b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/README.md new file mode 100644 index 0000000000..10f08ca677 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/README.md @@ -0,0 +1,106 @@ +# Helm Common library + +The common library is a way to unify the UX through all the Helm charts that implement it. + +The tooling suite that New Relic is huge and growing and this allows to set things globally +and locally for a single chart. + +## Documentation for chart writers + +If you are writing a chart that is going to use this library you can check the [developers guide](/library/common-library/DEVELOPERS.md) to see all +the functions/templates that we have implemented, what they do and how to use them. + +## Values managed globally + +We want to have a seamless experience through all the charts so we created this library that tries to standardize the behaviour +of all the charts. Sadly, because of the complexity of all these integrations, not all the charts behave exactly as expected. + +An example is `newrelic-infrastructure` that ignores `hostNetwork` in the control plane scraper because most of the users has the +control plane listening in the node to `localhost`. + +For each chart that has a special behavior (or further information of the behavior) there is a "chart particularities" section +in its README.md that explains which is the expected behavior. + +At the time of writing this, all the charts from `nri-bundle` except `newrelic-logging` and `synthetics-minion` implements this +library and honors global options as described in this document. + +Here is a list of global options: + +| Global keys | Local keys | Default | Merged[1](#values-managed-globally-1) | Description | +|-------------|------------|---------|--------------------------------------------------|-------------| +| global.cluster | cluster | `""` | | Name of the Kubernetes cluster monitored | +| global.licenseKey | licenseKey | `""` | | This set this license key to use | +| global.customSecretName | customSecretName | `""` | | In case you don't want to have the license key in you values, this allows you to point to a user created secret to get the key from there | +| global.customSecretLicenseKey | customSecretLicenseKey | `""` | | In case you don't want to have the license key in you values, this allows you to point to which secret key is the license key located | +| global.podLabels | podLabels | `{}` | yes | Additional labels for chart pods | +| global.labels | labels | `{}` | yes | Additional labels for chart objects | +| global.priorityClassName | priorityClassName | `""` | | Sets pod's priorityClassName | +| global.hostNetwork | hostNetwork | `false` | | Sets pod's hostNetwork | +| global.dnsConfig | dnsConfig | `{}` | | Sets pod's dnsConfig | +| global.images.registry | See [Further information](#values-managed-globally-2) | `""` | | Changes the registry where to get the images. Useful when there is an internal image cache/proxy | +| global.images.pullSecrets | See [Further information](#values-managed-globally-2) | `[]` | yes | Set secrets to be able to fetch images | +| global.podSecurityContext | podSecurityContext | `{}` | | Sets security context (at pod level) | +| global.containerSecurityContext | containerSecurityContext | `{}` | | Sets security context (at container level) | +| global.affinity | affinity | `{}` | | Sets pod/node affinities | +| global.nodeSelector | nodeSelector | `{}` | | Sets pod's node selector | +| global.tolerations | tolerations | `[]` | | Sets pod's tolerations to node taints | +| global.serviceAccount.create | serviceAccount.create | `true` | | Configures if the service account should be created or not | +| global.serviceAccount.name | serviceAccount.name | name of the release | | Change the name of the service account. This is honored if you disable on this cahrt the creation of the service account so you can use your own. | +| global.serviceAccount.annotations | serviceAccount.annotations | `{}` | yes | Add these annotations to the service account we create | +| global.customAttributes | customAttributes | `{}` | | Adds extra attributes to the cluster and all the metrics emitted to the backend | +| global.fedramp | fedramp | `false` | | Enables FedRAMP | +| global.lowDataMode | lowDataMode | `false` | | Reduces number of metrics sent in order to reduce costs | +| global.privileged | privileged | Depends on the chart | | In each integration it has different behavior. See [Further information](#values-managed-globally-3) but all aims to send less metrics to the backend to try to save costs | +| global.proxy | proxy | `""` | | Configures the integration to send all HTTP/HTTPS request through the proxy in that URL. The URL should have a standard format like `https://user:password@hostname:port` | +| global.nrStaging | nrStaging | `false` | | Send the metrics to the staging backend. Requires a valid staging license key | +| global.verboseLog | verboseLog | `false` | | Sets the debug/trace logs to this integration or all integrations if it is set globally | + +### Further information + +#### 1. Merged + +Merged means that the values from global are not replaced by the local ones. Think in this example: +```yaml +global: + labels: + global: global + hostNetwork: true + nodeSelector: + global: global + +labels: + local: local +nodeSelector: + local: local +hostNetwork: false +``` + +This values will template `hostNetwork` to `false`, a map of labels `{ "global": "global", "local": "local" }` and a `nodeSelector` with +`{ "local": "local" }`. + +As Helm by default merges all the maps it could be confusing that we have two behaviors (merging `labels` and replacing `nodeSelector`) +the `values` from global to local. This is the rationale behind this: +* `hostNetwork` is templated to `false` because is overriding the value defined globally. +* `labels` are merged because the user may want to label all the New Relic pods at once and label other solution pods differently for + clarity' sake. +* `nodeSelector` does not merge as `labels` because could make it harder to overwrite/delete a selector that comes from global because + of the logic that Helm follows merging maps. + + +#### 2. Fine grain registries + +Some charts only have 1 image while others that can have 2 or more images. The local path for the registry can change depending +on the chart itself. + +As this is mostly unique per helm chart, you should take a look to the chart's values table (or directly to the `values.yaml` file to see all the +images that you can change. + +This should only be needed if you have an advanced setup that forces you to have granularity enough to force a proxy/cache registry per integration. + + + +#### 3. Privileged mode + +By default, from the common library, the privileged mode is set to false. But most of the helm charts require this to be true to fetch more +metrics so could see a true in some charts. The consequences of the privileged mode differ from one chart to another so for each chart that +honors the privileged mode toggle should be a section in the README explaining which is the behavior with it enabled or disabled. diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_affinity.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_affinity.tpl new file mode 100644 index 0000000000..1b2636754e --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_affinity.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod affinity */ -}} +{{- define "newrelic.common.affinity" -}} + {{- if .Values.affinity -}} + {{- toYaml .Values.affinity -}} + {{- else if .Values.global -}} + {{- if .Values.global.affinity -}} + {{- toYaml .Values.global.affinity -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_agent-config.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_agent-config.tpl new file mode 100644 index 0000000000..9c32861a02 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_agent-config.tpl @@ -0,0 +1,26 @@ +{{/* +This helper should return the defaults that all agents should have +*/}} +{{- define "newrelic.common.agentConfig.defaults" -}} +{{- if include "newrelic.common.verboseLog" . }} +log: + level: trace +{{- end }} + +{{- if (include "newrelic.common.nrStaging" . ) }} +staging: true +{{- end }} + +{{- with include "newrelic.common.proxy" . }} +proxy: {{ . | quote }} +{{- end }} + +{{- with include "newrelic.common.fedramp.enabled" . }} +fedramp: {{ . }} +{{- end }} + +{{- with fromYaml ( include "newrelic.common.customAttributes" . ) }} +custom_attributes: + {{- toYaml . | nindent 2 }} +{{- end }} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_cluster.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_cluster.tpl new file mode 100644 index 0000000000..0197dd35a3 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_cluster.tpl @@ -0,0 +1,15 @@ +{{/* +Return the cluster +*/}} +{{- define "newrelic.common.cluster" -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} + +{{- if .Values.cluster -}} + {{- .Values.cluster -}} +{{- else if $global.cluster -}} + {{- $global.cluster -}} +{{- else -}} + {{ fail "There is not cluster name definition set neither in `.global.cluster' nor `.cluster' in your values.yaml. Cluster name is required." }} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_custom-attributes.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_custom-attributes.tpl new file mode 100644 index 0000000000..92020719c3 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_custom-attributes.tpl @@ -0,0 +1,17 @@ +{{/* +This will render custom attributes as a YAML ready to be templated or be used with `fromYaml`. +*/}} +{{- define "newrelic.common.customAttributes" -}} +{{- $customAttributes := dict -}} + +{{- $global := index .Values "global" | default dict -}} +{{- if $global.customAttributes -}} +{{- $customAttributes = mergeOverwrite $customAttributes $global.customAttributes -}} +{{- end -}} + +{{- if .Values.customAttributes -}} +{{- $customAttributes = mergeOverwrite $customAttributes .Values.customAttributes -}} +{{- end -}} + +{{- toYaml $customAttributes -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_dnsconfig.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_dnsconfig.tpl new file mode 100644 index 0000000000..d4e40aa8af --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_dnsconfig.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod dnsConfig */ -}} +{{- define "newrelic.common.dnsConfig" -}} + {{- if .Values.dnsConfig -}} + {{- toYaml .Values.dnsConfig -}} + {{- else if .Values.global -}} + {{- if .Values.global.dnsConfig -}} + {{- toYaml .Values.global.dnsConfig -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_fedramp.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_fedramp.tpl new file mode 100644 index 0000000000..9df8d6b5e9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_fedramp.tpl @@ -0,0 +1,25 @@ +{{- /* Defines the fedRAMP flag */ -}} +{{- define "newrelic.common.fedramp.enabled" -}} + {{- if .Values.fedramp -}} + {{- if .Values.fedramp.enabled -}} + {{- .Values.fedramp.enabled -}} + {{- end -}} + {{- else if .Values.global -}} + {{- if .Values.global.fedramp -}} + {{- if .Values.global.fedramp.enabled -}} + {{- .Values.global.fedramp.enabled -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} + + + +{{- /* Return FedRAMP value directly ready to be templated */ -}} +{{- define "newrelic.common.fedramp.enabled.value" -}} +{{- if include "newrelic.common.fedramp.enabled" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_hostnetwork.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_hostnetwork.tpl new file mode 100644 index 0000000000..4cf017ef7e --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_hostnetwork.tpl @@ -0,0 +1,39 @@ +{{- /* +Abstraction of the hostNetwork toggle. +This helper allows to override the global `.global.hostNetwork` with the value of `.hostNetwork`. +Returns "true" if `hostNetwork` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.hostNetwork" -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} + +{{- /* +`get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs + +We also want only to return when this is true, returning `false` here will template "false" (string) when doing +an `(include "newrelic.common.hostNetwork" .)`, which is not an "empty string" so it is `true` if it is used +as an evaluation somewhere else. +*/ -}} +{{- if get .Values "hostNetwork" | kindIs "bool" -}} + {{- if .Values.hostNetwork -}} + {{- .Values.hostNetwork -}} + {{- end -}} +{{- else if get $global "hostNetwork" | kindIs "bool" -}} + {{- if $global.hostNetwork -}} + {{- $global.hostNetwork -}} + {{- end -}} +{{- end -}} +{{- end -}} + + +{{- /* +Abstraction of the hostNetwork toggle. +This helper abstracts the function "newrelic.common.hostNetwork" to return true or false directly. +*/ -}} +{{- define "newrelic.common.hostNetwork.value" -}} +{{- if include "newrelic.common.hostNetwork" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_images.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_images.tpl new file mode 100644 index 0000000000..d4fb432905 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_images.tpl @@ -0,0 +1,94 @@ +{{- /* +Return the proper image name +{{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.path.to.the.image "defaultRegistry" "your.private.registry.tld" "context" .) }} +*/ -}} +{{- define "newrelic.common.images.image" -}} + {{- $registryName := include "newrelic.common.images.registry" ( dict "imageRoot" .imageRoot "defaultRegistry" .defaultRegistry "context" .context ) -}} + {{- $repositoryName := include "newrelic.common.images.repository" .imageRoot -}} + {{- $tag := include "newrelic.common.images.tag" ( dict "imageRoot" .imageRoot "context" .context) -}} + + {{- if $registryName -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag | quote -}} + {{- else -}} + {{- printf "%s:%s" $repositoryName $tag | quote -}} + {{- end -}} +{{- end -}} + + + +{{- /* +Return the proper image registry +{{ include "newrelic.common.images.registry" ( dict "imageRoot" .Values.path.to.the.image "defaultRegistry" "your.private.registry.tld" "context" .) }} +*/ -}} +{{- define "newrelic.common.images.registry" -}} +{{- $globalRegistry := "" -}} +{{- if .context.Values.global -}} + {{- if .context.Values.global.images -}} + {{- with .context.Values.global.images.registry -}} + {{- $globalRegistry = . -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- $localRegistry := "" -}} +{{- if .imageRoot.registry -}} + {{- $localRegistry = .imageRoot.registry -}} +{{- end -}} + +{{- $registry := $localRegistry | default $globalRegistry | default .defaultRegistry -}} +{{- if $registry -}} + {{- $registry -}} +{{- end -}} +{{- end -}} + + + +{{- /* +Return the proper image repository +{{ include "newrelic.common.images.repository" .Values.path.to.the.image }} +*/ -}} +{{- define "newrelic.common.images.repository" -}} + {{- .repository -}} +{{- end -}} + + + +{{- /* +Return the proper image tag +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.path.to.the.image "context" .) }} +*/ -}} +{{- define "newrelic.common.images.tag" -}} + {{- .imageRoot.tag | default .context.Chart.AppVersion | toString -}} +{{- end -}} + + + +{{- /* +Return the proper Image Pull Registry Secret Names evaluating values as templates +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" (list .Values.path.to.the.images.pullSecrets1, .Values.path.to.the.images.pullSecrets2) "context" .) }} +*/ -}} +{{- define "newrelic.common.images.renderPullSecrets" -}} + {{- $flatlist := list }} + + {{- if .context.Values.global -}} + {{- if .context.Values.global.images -}} + {{- if .context.Values.global.images.pullSecrets -}} + {{- range .context.Values.global.images.pullSecrets -}} + {{- $flatlist = append $flatlist . -}} + {{- end -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- range .pullSecrets -}} + {{- if not (empty .) -}} + {{- range . -}} + {{- $flatlist = append $flatlist . -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- if $flatlist -}} + {{- toYaml $flatlist -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_insights.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_insights.tpl new file mode 100644 index 0000000000..895c377326 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_insights.tpl @@ -0,0 +1,56 @@ +{{/* +Return the name of the secret holding the Insights Key. +*/}} +{{- define "newrelic.common.insightsKey.secretName" -}} +{{- $default := include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "insightskey" ) -}} +{{- include "newrelic.common.insightsKey._customSecretName" . | default $default -}} +{{- end -}} + +{{/* +Return the name key for the Insights Key inside the secret. +*/}} +{{- define "newrelic.common.insightsKey.secretKeyName" -}} +{{- include "newrelic.common.insightsKey._customSecretKey" . | default "insightsKey" -}} +{{- end -}} + +{{/* +Return local insightsKey if set, global otherwise. +This helper is for internal use. +*/}} +{{- define "newrelic.common.insightsKey._licenseKey" -}} +{{- if .Values.insightsKey -}} + {{- .Values.insightsKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.insightsKey -}} + {{- .Values.global.insightsKey -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name of the secret holding the Insights Key. +This helper is for internal use. +*/}} +{{- define "newrelic.common.insightsKey._customSecretName" -}} +{{- if .Values.customInsightsKeySecretName -}} + {{- .Values.customInsightsKeySecretName -}} +{{- else if .Values.global -}} + {{- if .Values.global.customInsightsKeySecretName -}} + {{- .Values.global.customInsightsKeySecretName -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name key for the Insights Key inside the secret. +This helper is for internal use. +*/}} +{{- define "newrelic.common.insightsKey._customSecretKey" -}} +{{- if .Values.customInsightsKeySecretKey -}} + {{- .Values.customInsightsKeySecretKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.customInsightsKeySecretKey }} + {{- .Values.global.customInsightsKeySecretKey -}} + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_insights_secret.yaml.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_insights_secret.yaml.tpl new file mode 100644 index 0000000000..556caa6ca6 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_insights_secret.yaml.tpl @@ -0,0 +1,21 @@ +{{/* +Renders the insights key secret if user has not specified a custom secret. +*/}} +{{- define "newrelic.common.insightsKey.secret" }} +{{- if not (include "newrelic.common.insightsKey._customSecretName" .) }} +{{- /* Fail if licenseKey is empty and required: */ -}} +{{- if not (include "newrelic.common.insightsKey._licenseKey" .) }} + {{- fail "You must specify a insightsKey or a customInsightsSecretName containing it" }} +{{- end }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "newrelic.common.insightsKey.secretName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +data: + {{ include "newrelic.common.insightsKey.secretKeyName" . }}: {{ include "newrelic.common.insightsKey._licenseKey" . | b64enc }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_labels.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_labels.tpl new file mode 100644 index 0000000000..b025948285 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_labels.tpl @@ -0,0 +1,54 @@ +{{/* +This will render the labels that should be used in all the manifests used by the helm chart. +*/}} +{{- define "newrelic.common.labels" -}} +{{- $global := index .Values "global" | default dict -}} + +{{- $chart := dict "helm.sh/chart" (include "newrelic.common.naming.chart" . ) -}} +{{- $managedBy := dict "app.kubernetes.io/managed-by" .Release.Service -}} +{{- $selectorLabels := fromYaml (include "newrelic.common.labels.selectorLabels" . ) -}} + +{{- $labels := mustMergeOverwrite $chart $managedBy $selectorLabels -}} +{{- if .Chart.AppVersion -}} +{{- $labels = mustMergeOverwrite $labels (dict "app.kubernetes.io/version" .Chart.AppVersion) -}} +{{- end -}} + +{{- $globalUserLabels := $global.labels | default dict -}} +{{- $localUserLabels := .Values.labels | default dict -}} + +{{- $labels = mustMergeOverwrite $labels $globalUserLabels $localUserLabels -}} + +{{- toYaml $labels -}} +{{- end -}} + + + +{{/* +This will render the labels that should be used in deployments/daemonsets template pods as a selector. +*/}} +{{- define "newrelic.common.labels.selectorLabels" -}} +{{- $name := dict "app.kubernetes.io/name" ( include "newrelic.common.naming.name" . ) -}} +{{- $instance := dict "app.kubernetes.io/instance" .Release.Name -}} + +{{- $selectorLabels := mustMergeOverwrite $name $instance -}} + +{{- toYaml $selectorLabels -}} +{{- end }} + + + +{{/* +Pod labels +*/}} +{{- define "newrelic.common.labels.podLabels" -}} +{{- $selectorLabels := fromYaml (include "newrelic.common.labels.selectorLabels" . ) -}} + +{{- $global := index .Values "global" | default dict -}} +{{- $globalPodLabels := $global.podLabels | default dict }} + +{{- $localPodLabels := .Values.podLabels | default dict }} + +{{- $podLabels := mustMergeOverwrite $selectorLabels $globalPodLabels $localPodLabels -}} + +{{- toYaml $podLabels -}} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_license.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_license.tpl new file mode 100644 index 0000000000..647b4ff439 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_license.tpl @@ -0,0 +1,56 @@ +{{/* +Return the name of the secret holding the License Key. +*/}} +{{- define "newrelic.common.license.secretName" -}} +{{- $default := include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "license" ) -}} +{{- include "newrelic.common.license._customSecretName" . | default $default -}} +{{- end -}} + +{{/* +Return the name key for the License Key inside the secret. +*/}} +{{- define "newrelic.common.license.secretKeyName" -}} +{{- include "newrelic.common.license._customSecretKey" . | default "licenseKey" -}} +{{- end -}} + +{{/* +Return local licenseKey if set, global otherwise. +This helper is for internal use. +*/}} +{{- define "newrelic.common.license._licenseKey" -}} +{{- if .Values.licenseKey -}} + {{- .Values.licenseKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.licenseKey -}} + {{- .Values.global.licenseKey -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name of the secret holding the License Key. +This helper is for internal use. +*/}} +{{- define "newrelic.common.license._customSecretName" -}} +{{- if .Values.customSecretName -}} + {{- .Values.customSecretName -}} +{{- else if .Values.global -}} + {{- if .Values.global.customSecretName -}} + {{- .Values.global.customSecretName -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name key for the License Key inside the secret. +This helper is for internal use. +*/}} +{{- define "newrelic.common.license._customSecretKey" -}} +{{- if .Values.customSecretLicenseKey -}} + {{- .Values.customSecretLicenseKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.customSecretLicenseKey }} + {{- .Values.global.customSecretLicenseKey -}} + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_license_secret.yaml.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_license_secret.yaml.tpl new file mode 100644 index 0000000000..610a0a3370 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_license_secret.yaml.tpl @@ -0,0 +1,21 @@ +{{/* +Renders the license key secret if user has not specified a custom secret. +*/}} +{{- define "newrelic.common.license.secret" }} +{{- if not (include "newrelic.common.license._customSecretName" .) }} +{{- /* Fail if licenseKey is empty and required: */ -}} +{{- if not (include "newrelic.common.license._licenseKey" .) }} + {{- fail "You must specify a licenseKey or a customSecretName containing it" }} +{{- end }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "newrelic.common.license.secretName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +data: + {{ include "newrelic.common.license.secretKeyName" . }}: {{ include "newrelic.common.license._licenseKey" . | b64enc }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_low-data-mode.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_low-data-mode.tpl new file mode 100644 index 0000000000..3dd55ef2ff --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_low-data-mode.tpl @@ -0,0 +1,26 @@ +{{- /* +Abstraction of the lowDataMode toggle. +This helper allows to override the global `.global.lowDataMode` with the value of `.lowDataMode`. +Returns "true" if `lowDataMode` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.lowDataMode" -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if (get .Values "lowDataMode" | kindIs "bool") -}} + {{- if .Values.lowDataMode -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.lowDataMode" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.lowDataMode -}} + {{- end -}} +{{- else -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "lowDataMode" | kindIs "bool" -}} + {{- if $global.lowDataMode -}} + {{- $global.lowDataMode -}} + {{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_naming.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_naming.tpl new file mode 100644 index 0000000000..19fa92648c --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_naming.tpl @@ -0,0 +1,73 @@ +{{/* +This is an function to be called directly with a string just to truncate strings to +63 chars because some Kubernetes name fields are limited to that. +*/}} +{{- define "newrelic.common.naming.truncateToDNS" -}} +{{- . | trunc 63 | trimSuffix "-" }} +{{- end }} + + + +{{- /* +Given a name and a suffix returns a 'DNS Valid' which always include the suffix, truncating the name if needed. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If suffix is too long it gets truncated but it always takes precedence over name, so a 63 chars suffix would suppress the name. +Usage: +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" "" "suffix" "my-suffix" ) }} +*/ -}} +{{- define "newrelic.common.naming.truncateToDNSWithSuffix" -}} +{{- $suffix := (include "newrelic.common.naming.truncateToDNS" .suffix) -}} +{{- $maxLen := (max (sub 63 (add1 (len $suffix))) 0) -}} {{- /* We prepend "-" to the suffix so an additional character is needed */ -}} + +{{- $newName := .name | trunc ($maxLen | int) | trimSuffix "-" -}} +{{- if $newName -}} +{{- printf "%s-%s" $newName $suffix -}} +{{- else -}} +{{ $suffix }} +{{- end -}} + +{{- end -}} + + + +{{/* +Expand the name of the chart. +Uses the Chart name by default if nameOverride is not set. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "newrelic.common.naming.name" -}} +{{- $name := .Values.nameOverride | default .Chart.Name -}} +{{- include "newrelic.common.naming.truncateToDNS" $name -}} +{{- end }} + + + +{{/* +Create a default fully qualified app name. +By default the full name will be "" just in if it has the chart name included in that, if not +it will be concatenated like "-". This could change if fullnameOverride or +nameOverride are set. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "newrelic.common.naming.fullname" -}} +{{- $name := include "newrelic.common.naming.name" . -}} + +{{- if .Values.fullnameOverride -}} + {{- $name = .Values.fullnameOverride -}} +{{- else if not (contains $name .Release.Name) -}} + {{- $name = printf "%s-%s" .Release.Name $name -}} +{{- end -}} + +{{- include "newrelic.common.naming.truncateToDNS" $name -}} + +{{- end -}} + + + +{{/* +Create chart name and version as used by the chart label. +This function should not be used for naming objects. Use "common.naming.{name,fullname}" instead. +*/}} +{{- define "newrelic.common.naming.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_nodeselector.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_nodeselector.tpl new file mode 100644 index 0000000000..d488873412 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_nodeselector.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod nodeSelector */ -}} +{{- define "newrelic.common.nodeSelector" -}} + {{- if .Values.nodeSelector -}} + {{- toYaml .Values.nodeSelector -}} + {{- else if .Values.global -}} + {{- if .Values.global.nodeSelector -}} + {{- toYaml .Values.global.nodeSelector -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_priority-class-name.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_priority-class-name.tpl new file mode 100644 index 0000000000..50182b7343 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_priority-class-name.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the pod priorityClassName */ -}} +{{- define "newrelic.common.priorityClassName" -}} + {{- if .Values.priorityClassName -}} + {{- .Values.priorityClassName -}} + {{- else if .Values.global -}} + {{- if .Values.global.priorityClassName -}} + {{- .Values.global.priorityClassName -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_privileged.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_privileged.tpl new file mode 100644 index 0000000000..f3ae814dd9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_privileged.tpl @@ -0,0 +1,28 @@ +{{- /* +This is a helper that returns whether the chart should assume the user is fine deploying privileged pods. +*/ -}} +{{- define "newrelic.common.privileged" -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists. */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if get .Values "privileged" | kindIs "bool" -}} + {{- if .Values.privileged -}} + {{- .Values.privileged -}} + {{- end -}} +{{- else if get $global "privileged" | kindIs "bool" -}} + {{- if $global.privileged -}} + {{- $global.privileged -}} + {{- end -}} +{{- end -}} +{{- end -}} + + + +{{- /* Return directly "true" or "false" based in the exist of "newrelic.common.privileged" */ -}} +{{- define "newrelic.common.privileged.value" -}} +{{- if include "newrelic.common.privileged" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_proxy.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_proxy.tpl new file mode 100644 index 0000000000..60f34c7ec1 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_proxy.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the proxy */ -}} +{{- define "newrelic.common.proxy" -}} + {{- if .Values.proxy -}} + {{- .Values.proxy -}} + {{- else if .Values.global -}} + {{- if .Values.global.proxy -}} + {{- .Values.global.proxy -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_security-context.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_security-context.tpl new file mode 100644 index 0000000000..9edfcabfd0 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_security-context.tpl @@ -0,0 +1,23 @@ +{{- /* Defines the container securityContext context */ -}} +{{- define "newrelic.common.securityContext.container" -}} +{{- $global := index .Values "global" | default dict -}} + +{{- if .Values.containerSecurityContext -}} + {{- toYaml .Values.containerSecurityContext -}} +{{- else if $global.containerSecurityContext -}} + {{- toYaml $global.containerSecurityContext -}} +{{- end -}} +{{- end -}} + + + +{{- /* Defines the pod securityContext context */ -}} +{{- define "newrelic.common.securityContext.pod" -}} +{{- $global := index .Values "global" | default dict -}} + +{{- if .Values.podSecurityContext -}} + {{- toYaml .Values.podSecurityContext -}} +{{- else if $global.podSecurityContext -}} + {{- toYaml $global.podSecurityContext -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_serviceaccount.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_serviceaccount.tpl new file mode 100644 index 0000000000..2d352f6ea9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_serviceaccount.tpl @@ -0,0 +1,90 @@ +{{- /* Defines if the service account has to be created or not */ -}} +{{- define "newrelic.common.serviceAccount.create" -}} +{{- $valueFound := false -}} + +{{- /* Look for a global creation of a service account */ -}} +{{- if get .Values "serviceAccount" | kindIs "map" -}} + {{- if (get .Values.serviceAccount "create" | kindIs "bool") -}} + {{- $valueFound = true -}} + {{- if .Values.serviceAccount.create -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.serviceAccount.name" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.serviceAccount.create -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- /* Look for a local creation of a service account */ -}} +{{- if not $valueFound -}} + {{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} + {{- $global := index .Values "global" | default dict -}} + {{- if get $global "serviceAccount" | kindIs "map" -}} + {{- if get $global.serviceAccount "create" | kindIs "bool" -}} + {{- $valueFound = true -}} + {{- if $global.serviceAccount.create -}} + {{- $global.serviceAccount.create -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- /* In case no serviceAccount value has been found, default to "true" */ -}} +{{- if not $valueFound -}} +true +{{- end -}} +{{- end -}} + + + +{{- /* Defines the name of the service account */ -}} +{{- define "newrelic.common.serviceAccount.name" -}} +{{- $localServiceAccount := "" -}} +{{- if get .Values "serviceAccount" | kindIs "map" -}} + {{- if (get .Values.serviceAccount "name" | kindIs "string") -}} + {{- $localServiceAccount = .Values.serviceAccount.name -}} + {{- end -}} +{{- end -}} + +{{- $globalServiceAccount := "" -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "serviceAccount" | kindIs "map" -}} + {{- if get $global.serviceAccount "name" | kindIs "string" -}} + {{- $globalServiceAccount = $global.serviceAccount.name -}} + {{- end -}} +{{- end -}} + +{{- if (include "newrelic.common.serviceAccount.create" .) -}} + {{- $localServiceAccount | default $globalServiceAccount | default (include "newrelic.common.naming.fullname" .) -}} +{{- else -}} + {{- $localServiceAccount | default $globalServiceAccount | default "default" -}} +{{- end -}} +{{- end -}} + + + +{{- /* Merge the global and local annotations for the service account */ -}} +{{- define "newrelic.common.serviceAccount.annotations" -}} +{{- $localServiceAccount := dict -}} +{{- if get .Values "serviceAccount" | kindIs "map" -}} + {{- if get .Values.serviceAccount "annotations" -}} + {{- $localServiceAccount = .Values.serviceAccount.annotations -}} + {{- end -}} +{{- end -}} + +{{- $globalServiceAccount := dict -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "serviceAccount" | kindIs "map" -}} + {{- if get $global.serviceAccount "annotations" -}} + {{- $globalServiceAccount = $global.serviceAccount.annotations -}} + {{- end -}} +{{- end -}} + +{{- $merged := mustMergeOverwrite $globalServiceAccount $localServiceAccount -}} + +{{- if $merged -}} + {{- toYaml $merged -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_staging.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_staging.tpl new file mode 100644 index 0000000000..bd9ad09bb9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_staging.tpl @@ -0,0 +1,39 @@ +{{- /* +Abstraction of the nrStaging toggle. +This helper allows to override the global `.global.nrStaging` with the value of `.nrStaging`. +Returns "true" if `nrStaging` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.nrStaging" -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if (get .Values "nrStaging" | kindIs "bool") -}} + {{- if .Values.nrStaging -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.nrStaging" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.nrStaging -}} + {{- end -}} +{{- else -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "nrStaging" | kindIs "bool" -}} + {{- if $global.nrStaging -}} + {{- $global.nrStaging -}} + {{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} + + + +{{- /* +Returns "true" of "false" directly instead of empty string (Helm falsiness) based on the exit of "newrelic.common.nrStaging" +*/ -}} +{{- define "newrelic.common.nrStaging.value" -}} +{{- if include "newrelic.common.nrStaging" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_tolerations.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_tolerations.tpl new file mode 100644 index 0000000000..e016b38e27 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_tolerations.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod tolerations */ -}} +{{- define "newrelic.common.tolerations" -}} + {{- if .Values.tolerations -}} + {{- toYaml .Values.tolerations -}} + {{- else if .Values.global -}} + {{- if .Values.global.tolerations -}} + {{- toYaml .Values.global.tolerations -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_verbose-log.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_verbose-log.tpl new file mode 100644 index 0000000000..2286d46815 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/templates/_verbose-log.tpl @@ -0,0 +1,54 @@ +{{- /* +Abstraction of the verbose toggle. +This helper allows to override the global `.global.verboseLog` with the value of `.verboseLog`. +Returns "true" if `verbose` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.verboseLog" -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if (get .Values "verboseLog" | kindIs "bool") -}} + {{- if .Values.verboseLog -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.verboseLog" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.verboseLog -}} + {{- end -}} +{{- else -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "verboseLog" | kindIs "bool" -}} + {{- if $global.verboseLog -}} + {{- $global.verboseLog -}} + {{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} + + + +{{- /* +Abstraction of the verbose toggle. +This helper abstracts the function "newrelic.common.verboseLog" to return true or false directly. +*/ -}} +{{- define "newrelic.common.verboseLog.valueAsBoolean" -}} +{{- if include "newrelic.common.verboseLog" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} + + + +{{- /* +Abstraction of the verbose toggle. +This helper abstracts the function "newrelic.common.verboseLog" to return 1 or 0 directly. +*/ -}} +{{- define "newrelic.common.verboseLog.valueAsInt" -}} +{{- if include "newrelic.common.verboseLog" . -}} +1 +{{- else -}} +0 +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/values.yaml new file mode 100644 index 0000000000..75e2d112ad --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/charts/common-library/values.yaml @@ -0,0 +1 @@ +# values are not needed for the library chart, however this file is still needed for helm lint to work. diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/ci/test-enable-windows-values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/ci/test-enable-windows-values.yaml new file mode 100644 index 0000000000..870bc082ac --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/ci/test-enable-windows-values.yaml @@ -0,0 +1,2 @@ +enableLinux: false +enableWindows: true diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/ci/test-lowdatamode-values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/ci/test-lowdatamode-values.yaml new file mode 100644 index 0000000000..7740338b05 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/ci/test-lowdatamode-values.yaml @@ -0,0 +1 @@ +lowDataMode: true diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/ci/test-override-global-lowdatamode.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/ci/test-override-global-lowdatamode.yaml new file mode 100644 index 0000000000..22dd7e05e5 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/ci/test-override-global-lowdatamode.yaml @@ -0,0 +1,3 @@ +global: + lowDataMode: true +lowDataMode: false diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/ci/test-staging-values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/ci/test-staging-values.yaml new file mode 100644 index 0000000000..efbdccaf81 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/ci/test-staging-values.yaml @@ -0,0 +1 @@ +nrStaging: true diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/ci/test-with-empty-global.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/ci/test-with-empty-global.yaml new file mode 100644 index 0000000000..490a0b7ed3 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/ci/test-with-empty-global.yaml @@ -0,0 +1 @@ +global: {} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/ci/test-with-empty-values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/ci/test-with-empty-values.yaml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/fluent-bit-and-plugin-metrics-dashboard-template.json b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/fluent-bit-and-plugin-metrics-dashboard-template.json new file mode 100644 index 0000000000..cafdaf85ca --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/fluent-bit-and-plugin-metrics-dashboard-template.json @@ -0,0 +1,2237 @@ +{ + "name": "Kubernetes Fluent Bit monitoring", + "description": null, + "permissions": "PUBLIC_READ_WRITE", + "pages": [ + { + "name": "Fluent Bit metrics: General", + "description": null, + "widgets": [ + { + "title": "", + "layout": { + "column": 1, + "row": 1, + "width": 6, + "height": 6 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.markdown" + }, + "rawConfiguration": { + "text": "# README\n\n## About this page\nThis page represents most of [Fluent Bit's internal metrics](https://docs.fluentbit.io/manual/administration/monitoring#for-v2-metrics). The metric representations are grouped by categories and faceted by each plugin instance where appropriate.\n\n## How to filter\n1. Select the Kubernetes cluster you want to troubleshoot in the \"Cluster Name\" variable above.\n2. [OPTIONAL] You can use any of the values in the `Node name` and `Pod name` columns on the \"Fluent Bit version\" table to further filter the metrics displayed in the graphs below. To do so, you need to enable [facet filtering](https://docs.newrelic.com/docs/query-your-data/explore-query-data/dashboards/filter-new-relic-one-dashboards-facets/) on that table by clicking on the \"Edit\" submenu and select \"Filter the current dashboard\" under \"Facet Linking\". \n\n## Legend\n### Metric dimensions\n- **name**: the name of the Fluent Bit plugin. Version 1.21.0 of our Helm chart names them according to the plugin names described in the following section.\n- **pod_name**: the `newrelic-logging` pod (Fluent Bit instance) that emitted this metric.\n- **node_name**: physical Kubernetes node where the `newrelic-logging` pod is running.\n\n### Plugin names\n- **pod-logs-tailer**: `tail` *INPUT* plugin normally reading from `/var/log/containers/*.log`\n- **kubernetes-enricher**: `kubernetes` *FILTER* plugin that queries the Kubernetes API to enrich the logs with pod/container metadata.\n- **node-attributes-enricher**: `record_modifier` *FILTER* plugin that enriches logs with `cluster_name`.\n- **kubernetes-attribute-lifter** (only when in low data mode): `nest` *FILTER* plugin that lifts all the keys under `kubernetes`. This plugin is transparent to the final shape of the log.\n- **node-attributes-enricher-filter** (only when in low data mode): same as node-attributes-enricher`, but it also removes attributes that are not strictly necessary for correct platform functioning.\n- **newrelic-logs-forwarder**: `newrelic` *OUTPUT* plugin that sends logs to the New Relic Logs API" + } + }, + { + "title": "Fluent Bit version", + "layout": { + "column": 7, + "row": 1, + "width": 6, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.table" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT latest(os) as 'OS', latest(version) as 'FB version', latest(cluster_name) FROM Metric where metricName = 'fluentbit_build_info' AND cluster_name IN ({{cluster_name}}) since 1 hour ago facet pod_name, node_name limit max" + } + ], + "platformOptions": { + "ignoreTimeRange": false + } + } + }, + { + "title": "Fluent Bit uptime", + "layout": { + "column": 7, + "row": 4, + "width": 6, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT latest(fluentbit_uptime) FROM Metric where cluster_name IN ({{cluster_name}}) facet pod_name timeseries" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "", + "layout": { + "column": 1, + "row": 7, + "width": 12, + "height": 1 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.markdown" + }, + "rawConfiguration": { + "text": "# INPUTS" + } + }, + { + "title": "Input byte rate (bytes/minute)", + "layout": { + "column": 1, + "row": 8, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT rate(sum(fluentbit_input_bytes_total), 1 minute) as 'bytes/minute' FROM Metric where name != 'fb-metrics-collector' and cluster_name IN ({{cluster_name}}) timeseries max facet name, pod_name" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "Input log rate (records/minute)", + "layout": { + "column": 5, + "row": 8, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT rate(sum(fluentbit_input_records_total), 1 minute) as 'logs/minute' FROM Metric where name != 'fb-metrics-collector' and cluster_name IN ({{cluster_name}}) facet name, pod_name timeseries max" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "Average incoming record size (bytes)", + "layout": { + "column": 9, + "row": 8, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT sum(fluentbit_input_bytes_total)/sum(fluentbit_input_records_total) as 'Average incoming record size (bytes)' FROM Metric where name != 'fb-metrics-collector' and cluster_name IN ({{cluster_name}}) facet name, pod_name timeseries max" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "units": { + "unit": "BYTES" + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "", + "layout": { + "column": 1, + "row": 11, + "width": 12, + "height": 1 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.markdown" + }, + "rawConfiguration": { + "text": "# FILTERS" + } + }, + { + "title": "Filter byte rate (bytes/minute)", + "layout": { + "column": 1, + "row": 12, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT rate(sum(fluentbit_filter_bytes_total), 1 minute) FROM Metric WHERE cluster_name IN ({{cluster_name}}) facet name, pod_name timeseries max" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "Filter log rate (records/minute)", + "layout": { + "column": 5, + "row": 12, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT rate(sum(fluentbit_filter_records_total), 1 minute) FROM Metric WHERE cluster_name IN ({{cluster_name}}) facet name, pod_name timeseries max" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "Average filtered record size (bytes)", + "layout": { + "column": 9, + "row": 12, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT sum(fluentbit_filter_bytes_total)/sum(fluentbit_filter_records_total) AS 'Average filtered record size (bytes)' FROM Metric WHERE cluster_name IN ({{cluster_name}}) facet name, pod_name timeseries max" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "units": { + "unit": "BYTES" + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "Record add/drop rate per FILTER plugin", + "layout": { + "column": 1, + "row": 15, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT rate(sum(fluentbit_filter_add_records_total), 1 minute) as 'Added back to pipeline', rate(sum(fluentbit_filter_drop_records_total), 1 minute) as 'Removed from pipeline' FROM Metric WHERE cluster_name IN ({{cluster_name}}) facet name, pod_name timeseries MAX" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "units": { + "unit": "REQUESTS_PER_MINUTE" + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "", + "layout": { + "column": 1, + "row": 18, + "width": 12, + "height": 1 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.markdown" + }, + "rawConfiguration": { + "text": "# OUTPUTS" + } + }, + { + "title": "Output byte rate (bytes/minute)", + "layout": { + "column": 1, + "row": 19, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT rate(sum(fluentbit_output_proc_bytes_total), 1 minute) as 'bytes/minute' FROM Metric where cluster_name IN ({{cluster_name}}) AND name != 'fb-metrics-forwarder' facet name, pod_name timeseries max" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "Output log rate (records/minute)", + "layout": { + "column": 5, + "row": 19, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT rate(sum(fluentbit_output_proc_records_total), 1 minute) as 'records/minute' FROM Metric where cluster_name IN ({{cluster_name}}) AND name != 'fb-metrics-forwarder' facet name, pod_name timeseries MAX " + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "Average outgoing record size (bytes)", + "layout": { + "column": 9, + "row": 19, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT sum(fluentbit_output_proc_bytes_total)/sum(fluentbit_output_proc_records_total) as 'bytes' FROM Metric where cluster_name IN ({{cluster_name}}) AND name != 'fb-metrics-forwarder' facet name, pod_name timeseries MAX" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "units": { + "unit": "BYTES" + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "newrelic plugin statistics (records/minute)", + "layout": { + "column": 1, + "row": 22, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT rate(sum(fluentbit_output_proc_records_total), 1 minute) as 'Processed', rate(sum(fluentbit_output_dropped_records_total), 1 minute) as 'Dropped', rate(sum(fluentbit_output_retried_records_total), 1 minute) as 'Retried' FROM Metric where cluster_name IN ({{cluster_name}}) AND name = 'newrelic-logs-forwarder' facet pod_name timeseries max" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "Other OUTPUT plugin statistics (records/minute)", + "layout": { + "column": 5, + "row": 22, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT rate(sum(fluentbit_output_proc_records_total), 1 minute) as 'Processed', rate(sum(fluentbit_output_dropped_records_total), 1 minute) as 'Dropped', rate(sum(fluentbit_output_retried_records_total), 1 minute) as 'Retried' FROM Metric where cluster_name IN ({{cluster_name}}) AND name != 'newrelic-logs-forwarder' and name != 'fb-metrics-forwarder' facet name, pod_name timeseries max" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "Connections per OUTPUT plugin", + "layout": { + "column": 9, + "row": 22, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT max(fluentbit_output_upstream_total_connections) as 'Total', max(fluentbit_output_upstream_busy_connections) as 'Busy' FROM Metric where cluster_name IN ({{cluster_name}}) AND name != 'fb-metrics-forwarder' facet name, pod_name timeseries MAX" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "newrelic plugin errors (errors/minute)", + "layout": { + "column": 1, + "row": 25, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT rate(sum(fluentbit_output_errors_total), 1 minute) AS 'Errors/minute' FROM Metric where cluster_name IN ({{cluster_name}}) AND name = 'newrelic-logs-forwarder' facet pod_name timeseries MAX " + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "newrelic plugin chunk retry statistics (retries/minute)", + "layout": { + "column": 5, + "row": 25, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT rate(sum(fluentbit_output_retries_total), 1 minute) as 'Retries', rate(sum(fluentbit_output_retries_failed_total), 1 minute) as 'Expirations' FROM Metric where cluster_name IN ({{cluster_name}}) AND name = 'newrelic-logs-forwarder' facet pod_name timeseries max" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "", + "layout": { + "column": 1, + "row": 28, + "width": 12, + "height": 1 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.markdown" + }, + "rawConfiguration": { + "text": "# MEMORY USAGE" + } + }, + { + "title": "Input plugin memory usage", + "layout": { + "column": 1, + "row": 29, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT max(fluentbit_input_storage_memory_bytes) as 'Max' FROM Metric where cluster_name IN ({{cluster_name}}) and name != 'fb-metrics-collector' timeseries max facet name, pod_name " + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "units": { + "unit": "BYTES" + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "INPUT memory buffer over limit", + "layout": { + "column": 5, + "row": 29, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "colors": { + "seriesOverrides": [ + { + "color": "#013ef4", + "seriesName": "pod-logs-tailer" + } + ] + }, + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT max(fluentbit_input_storage_overlimit) FROM Metric where cluster_name IN ({{cluster_name}}) and name != 'fb-metrics-collector' timeseries max facet name, pod_name" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true, + "thresholds": [ + { + "from": 0.95, + "name": "Mem buf overlimit", + "severity": "critical", + "to": 1.05 + } + ] + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "Chunk statistics per INPUT plugin", + "layout": { + "column": 9, + "row": 29, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT average(fluentbit_input_storage_chunks_up) AS 'Up (in memory)', average(fluentbit_input_storage_chunks_down) AS 'Down (in fs)', average(fluentbit_input_storage_chunks_busy) AS 'Busy', average(fluentbit_input_storage_chunks) as 'Total' FROM Metric where name != 'fb-metrics-collector' since 1 hour ago timeseries MAX facet name, pod_name " + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "Buffered chunks", + "layout": { + "column": 1, + "row": 32, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT max(fluentbit_input_storage_chunks) AS 'Total', max(fluentbit_storage_mem_chunks) AS 'Memory', max(fluentbit_storage_fs_chunks) AS 'Filesystem' FROM Metric where cluster_name IN ({{cluster_name}}) facet pod_name timeseries MAX " + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "Busy chunks' size", + "layout": { + "column": 5, + "row": 32, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT max(fluentbit_input_storage_chunks_busy_bytes) FROM Metric where name != 'fb-metrics-collector' facet name, pod_name timeseries MAX since 1 hour ago" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "units": { + "unit": "BYTES" + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "Filesystem chunks state", + "layout": { + "column": 9, + "row": 32, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT average(fluentbit_storage_fs_chunks_up) AS 'Up (in memory)', average(fluentbit_storage_fs_chunks_down) AS 'Down (fs only)' FROM Metric since '2024-02-29 13:22:00+0000' UNTIL '2024-02-29 14:31:00+0000' timeseries MAX " + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + } + ] + }, + { + "name": "Fluent Bit metrics: Pipeline View", + "description": null, + "widgets": [ + { + "title": "", + "layout": { + "column": 1, + "row": 1, + "width": 6, + "height": 6 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.markdown" + }, + "rawConfiguration": { + "text": "# README\n\n## About this page\nThis page represents the same metrics that are displayed in the \"Fluent Bit metrics: General\" page. Nevertheless, they are grouped differently to allow you to visualize a given metric across the whole pipeline with a single glance.\n\n## How to filter\n1. Select the Kubernetes cluster you want to troubleshoot in the \"Cluster Name\" variable above.\n2. [OPTIONAL] You can use any of the values in the `Node name` and `Pod name` columns on the \"Fluent Bit version\" table to further filter the metrics displayed in the graphs below. To do so, you need to enable [facet filtering](https://docs.newrelic.com/docs/query-your-data/explore-query-data/dashboards/filter-new-relic-one-dashboards-facets/) on that table by clicking on the \"Edit\" submenu and select \"Filter the current dashboard\" under \"Facet Linking\". \n\n## Legend\n### Metric dimensions\n- **name**: the name of the Fluent Bit plugin. Version 1.21.0 of our Helm chart names them according to the plugin names described in the following section.\n- **pod_name**: the `newrelic-logging` pod (Fluent Bit instance) that emitted this metric.\n- **node_name**: physical Kubernetes node where the `newrelic-logging` pod is running.\n\n### Plugin names\n- **pod-logs-tailer**: `tail` *INPUT* plugin normally reading from `/var/log/containers/*.log`\n- **kubernetes-enricher**: `kubernetes` *FILTER* plugin that queries the Kubernetes API to enrich the logs with pod/container metadata.\n- **node-attributes-enricher**: `record_modifier` *FILTER* plugin that enriches logs with `cluster_name`.\n- **kubernetes-attribute-lifter** (only when in low data mode): `nest` *FILTER* plugin that lifts all the keys under `kubernetes`. This plugin is transparent to the final shape of the log.\n- **node-attributes-enricher-filter** (only when in low data mode): same as node-attributes-enricher`, but it also removes attributes that are not strictly necessary for correct platform functioning.\n- **newrelic-logs-forwarder**: `newrelic` *OUTPUT* plugin that sends logs to the New Relic Logs API" + } + }, + { + "title": "Fluent Bit version", + "layout": { + "column": 7, + "row": 1, + "width": 6, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.table" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT latest(os) as 'OS', latest(version) as 'FB version', latest(cluster_name) FROM Metric where metricName = 'fluentbit_build_info' AND cluster_name IN ({{cluster_name}}) since 1 hour ago facet pod_name, node_name limit max" + } + ], + "platformOptions": { + "ignoreTimeRange": false + } + } + }, + { + "title": "Fluent Bit uptime", + "layout": { + "column": 7, + "row": 4, + "width": 6, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT latest(fluentbit_uptime) FROM Metric where cluster_name IN ({{cluster_name}}) timeseries facet pod_name " + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "units": { + "unit": "SECONDS" + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "", + "layout": { + "column": 1, + "row": 7, + "width": 12, + "height": 1 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.markdown" + }, + "rawConfiguration": { + "text": "# BYTE RATES" + } + }, + { + "title": "Input byte rate (bytes/minute)", + "layout": { + "column": 1, + "row": 8, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT rate(sum(fluentbit_input_bytes_total), 1 minute) as 'bytes/minute' FROM Metric where name != 'fb-metrics-collector' and cluster_name IN ({{cluster_name}}) timeseries max facet name, pod_name" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "Filter byte rate (bytes/minute)", + "layout": { + "column": 5, + "row": 8, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT rate(sum(fluentbit_filter_bytes_total), 1 minute) FROM Metric WHERE cluster_name IN ({{cluster_name}}) facet name, pod_name timeseries max" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "Output byte rate (bytes/minute)", + "layout": { + "column": 9, + "row": 8, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT rate(sum(fluentbit_output_proc_bytes_total), 1 minute) as 'bytes/minute' FROM Metric where cluster_name IN ({{cluster_name}}) AND name != 'fb-metrics-forwarder' facet name, pod_name timeseries max" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "", + "layout": { + "column": 1, + "row": 11, + "width": 12, + "height": 1 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.markdown" + }, + "rawConfiguration": { + "text": "# LOG RECORD RATES" + } + }, + { + "title": "Input log rate (records/minute)", + "layout": { + "column": 1, + "row": 12, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT rate(sum(fluentbit_input_records_total), 1 minute) as 'logs/minute' FROM Metric where name != 'fb-metrics-collector' and cluster_name IN ({{cluster_name}}) facet name, pod_name timeseries max" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "Filter log rate (records/minute)", + "layout": { + "column": 5, + "row": 12, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT rate(sum(fluentbit_filter_records_total), 1 minute) FROM Metric WHERE cluster_name IN ({{cluster_name}}) facet name, pod_name timeseries max" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "Output log rate (records/minute)", + "layout": { + "column": 9, + "row": 12, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT rate(sum(fluentbit_output_proc_records_total), 1 minute) as 'records/minute' FROM Metric where cluster_name IN ({{cluster_name}}) AND name != 'fb-metrics-forwarder' facet name, pod_name timeseries MAX " + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "Record add/drop rate per FILTER plugin", + "layout": { + "column": 5, + "row": 15, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT rate(sum(fluentbit_filter_add_records_total), 1 minute) as 'Added back to pipeline', rate(sum(fluentbit_filter_drop_records_total), 1 minute) as 'Removed from pipeline' FROM Metric WHERE cluster_name IN ({{cluster_name}}) facet name, pod_name timeseries MAX" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "units": { + "unit": "REQUESTS_PER_MINUTE" + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "", + "layout": { + "column": 1, + "row": 18, + "width": 12, + "height": 1 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.markdown" + }, + "rawConfiguration": { + "text": "# AVERAGE LOG RECORD SIZES AT THE END OF EACH STAGE" + } + }, + { + "title": "Average incoming record size (bytes)", + "layout": { + "column": 1, + "row": 19, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT sum(fluentbit_input_bytes_total)/sum(fluentbit_input_records_total) as 'Average incoming record size (bytes)' FROM Metric where name != 'fb-metrics-collector' and cluster_name IN ({{cluster_name}}) facet name, pod_name timeseries max" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "units": { + "unit": "BYTES" + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "Average filtered record size (bytes)", + "layout": { + "column": 5, + "row": 19, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT sum(fluentbit_filter_bytes_total)/sum(fluentbit_filter_records_total) AS 'Average filtered record size (bytes)' FROM Metric WHERE cluster_name IN ({{cluster_name}}) facet name, pod_name timeseries max" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "units": { + "unit": "BYTES" + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "Average outgoing record size (bytes)", + "layout": { + "column": 9, + "row": 19, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT sum(fluentbit_output_proc_bytes_total)/sum(fluentbit_output_proc_records_total) as 'bytes' FROM Metric where cluster_name IN ({{cluster_name}}) AND name != 'fb-metrics-forwarder' facet name, pod_name timeseries MAX" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "units": { + "unit": "BYTES" + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "", + "layout": { + "column": 1, + "row": 22, + "width": 12, + "height": 1 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.markdown" + }, + "rawConfiguration": { + "text": "# MEMORY USAGE AND BACKPRESSURE" + } + }, + { + "title": "Input plugin memory usage", + "layout": { + "column": 1, + "row": 23, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT max(fluentbit_input_storage_memory_bytes) as 'Max' FROM Metric where cluster_name IN ({{cluster_name}}) and name != 'fb-metrics-collector' timeseries max facet name, pod_name " + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "units": { + "unit": "BYTES" + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "Busy chunks' size", + "layout": { + "column": 5, + "row": 23, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT max(fluentbit_input_storage_chunks_busy_bytes) FROM Metric where name != 'fb-metrics-collector' facet name, pod_name timeseries MAX since 1 hour ago" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "units": { + "unit": "BYTES" + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "newrelic plugin chunk retry statistics (retries/minute)", + "layout": { + "column": 9, + "row": 23, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT rate(sum(fluentbit_output_retries_total), 1 minute) as 'Retries', rate(sum(fluentbit_output_retries_failed_total), 1 minute) as 'Expirations' FROM Metric where cluster_name IN ({{cluster_name}}) AND name = 'newrelic-logs-forwarder' facet pod_name timeseries max" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "INPUT memory buffer over limit", + "layout": { + "column": 1, + "row": 26, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "colors": { + "seriesOverrides": [ + { + "color": "#013ef4", + "seriesName": "pod-logs-tailer" + } + ] + }, + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT max(fluentbit_input_storage_overlimit) FROM Metric where cluster_name IN ({{cluster_name}}) and name != 'fb-metrics-collector' timeseries max facet name, pod_name" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true, + "thresholds": [ + { + "from": 0.95, + "name": "Mem buf overlimit", + "severity": "critical", + "to": 1.05 + } + ] + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "Chunk statistics per INPUT plugin", + "layout": { + "column": 5, + "row": 26, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT average(fluentbit_input_storage_chunks_up) AS 'Up (in memory)', average(fluentbit_input_storage_chunks_down) AS 'Down (in fs)', average(fluentbit_input_storage_chunks_busy) AS 'Busy', average(fluentbit_input_storage_chunks) as 'Total' FROM Metric where name != 'fb-metrics-collector' since 1 hour ago timeseries MAX facet name, pod_name " + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "newrelic plugin errors (errors/minute)", + "layout": { + "column": 9, + "row": 26, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT rate(sum(fluentbit_output_errors_total), 1 minute) AS 'Errors/minute' FROM Metric where cluster_name IN ({{cluster_name}}) AND name = 'newrelic-logs-forwarder' facet pod_name timeseries MAX " + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": { + "isLabelVisible": true + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + } + ] + }, + { + "name": "newrelic-fluent-bit-output plugin metrics", + "description": null, + "widgets": [ + { + "title": "", + "layout": { + "column": 1, + "row": 1, + "width": 4, + "height": 9 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.markdown" + }, + "rawConfiguration": { + "text": "# README\n## About this page\nThis page displays metrics collected internally in the [New Relic Fluent Bit output plugin](https://github.com/newrelic/newrelic-fluent-bit-output) (in short, **NR FB plugin**). These metrics are independent of Fluent Bit's, and **must not be considered as a stable API: they can change its naming or dimensions at any time in newer plugin versions**.\n\nPlease note that **the NR FB plugin does not include the `pod_name` nor the `node_name` dimensions**. Therefore, the graphs below represent an aggregation of all your running Fluent Bit instances across one or more clusters. You can use the `cluster_name` dimension (or dashboard variable above) to narrow down the troubleshooting to one or more clusters.\n\n## Basic naming conventions\n- Fluent Bit aggregates logs in batches, also referred as **[chunks](https://docs.fluentbit.io/manual/administration/buffering-and-storage#chunks-memory-filesystem-and-backpressure)**. Each chunk therefore contains an unknown amount of logs.\n- Chunks are received sequentially at the NR FB plugin, which takes care of reading the logs they contain and splitting them into the so-called New Relic *payloads*.\n- Each **payload** is a compressed stream of bytes that can be [at most 1MB long](https://docs.newrelic.com/docs/logs/log-api/introduction-log-api/#limits), and follows the [data format required by the Logs API](https://docs.newrelic.com/docs/logs/log-api/introduction-log-api/#json-content).\n\n\n## Error-detection graphs and recommended actions\n\nThe following are the main graphs used to detect potential problems in your log forwarding setup. Refer to each section to learn the recommended actions for each graph.\n\n### Payload packaging errors\nRepresents the percentage of Fluent Bit chunks that threw an error when they were attempted to be packaged as New Relic payloads. Such errors are never expected to happen. Therefore, **any value greater than 0% should be thoroughly investigated**.\n\nIf you find errors in this graph, please open a support ticket and include a sample of your logs for further investigation.\n\n### Payload sending errors\nRepresents the percentage of New Relic payloads that threw an unexpected error when they were attempted to be sent to New Relic. Such errors can happen sporadically: timeouts due to poor network performance or sudden network changes can cause them from time to time. Observing **values greater than 0% can sometimes be normal, but any value above 10% should be considered as an annomalous situation and should be thoroughly investigated**.\n\nIf you find errors in this graph, please ensure that you don't have any weak spots in your network path to New Relic: are you using a proxy? Is it or any network hop introducing too much latency due to being saturated? If you can't find anything on you side, please open a support ticket and include as much information as possible from your network setup.\n\n### Payload send results\nRepresents the amount of API requests that were performed to send logs to New Relic. **Ideally, you should only observe 202 responses here**. Sometimes, intermediary CDN providers can introduce some errors (503 error codes) from time to time, in which case your logs will not be lost and reattempted to be sent.\n\nIf you find a considerable amount of non-202 responses in this graph, please open a customer support ticket.\n\n## Additional troubleshooting graphs\n\nThe following graphs include additional fine-grained information that will be useful for New Relic to troubleshoot your potential installation issues.\n\n### Average timings\nRepresents the average amount of time the plugin spent packaging the log payloads and sending them to New Relic, respectively.\n\n### Accumulated time per minute\nRepresents the amount of time per minute the plugin spent packaging the log payloads and sending them to New Relic, respectively.\n\n### Payload size\nRepresents the size in bytes of the individual compressed payloads sent to New Relic.\n\n### Payload packets per Fluent Bit chunk\nRepresents the amount of payloads sent to New Relic per each Fluent Bit chunk." + } + }, + { + "title": "Payload packaging errors", + "layout": { + "column": 5, + "row": 1, + "width": 2, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.billboard" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "FROM Metric SELECT percentage(count(`logs.fb.packaging.time`), WHERE hasError = true) AS 'packaging errors'" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": [ + { + "alertSeverity": "CRITICAL", + "value": 0 + } + ] + } + }, + { + "title": "Payload sending errors", + "layout": { + "column": 7, + "row": 1, + "width": 2, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.billboard" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "FROM Metric SELECT percentage(count(`logs.fb.payload.send.time`), WHERE hasError = true) AS 'send errors'" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "thresholds": [ + { + "alertSeverity": "WARNING", + "value": 0 + }, + { + "alertSeverity": "CRITICAL", + "value": 0.1 + } + ] + } + }, + { + "title": "Payload send results", + "layout": { + "column": 9, + "row": 1, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT rate(count(logs.fb.payload.send.time), 1 minute) AS 'Status Code' FROM Metric FACET CASES(WHERE statusCode = 0 AS 'Send error') OR statusCode timeseries max" + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "units": { + "unit": "REQUESTS_PER_MINUTE" + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "Average timings", + "layout": { + "column": 5, + "row": 4, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT average(logs.fb.payload.send.time) AS 'Payload sending', average(logs.fb.packaging.time) AS 'Payload packaging' FROM Metric timeseries max" + } + ], + "nullValues": { + "nullValue": "zero" + }, + "platformOptions": { + "ignoreTimeRange": false + }, + "units": { + "unit": "MS" + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "Accumulated time per minute", + "layout": { + "column": 9, + "row": 4, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.area" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT rate(sum(logs.fb.total.send.time), 1 minute) AS 'Sending', rate(sum(logs.fb.packaging.time), 1 minute) AS 'Packaging' FROM Metric TIMESERIES max" + } + ], + "nullValues": { + "nullValue": "zero" + }, + "platformOptions": { + "ignoreTimeRange": false + }, + "units": { + "unit": "MS" + } + } + }, + { + "title": "Payload size", + "layout": { + "column": 5, + "row": 7, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT min(logs.fb.payload.size) AS 'Minimum', average(logs.fb.payload.size) AS 'Average', max(logs.fb.payload.size) AS 'Maximum' FROM Metric timeseries MAX " + } + ], + "nullValues": { + "nullValue": "default" + }, + "platformOptions": { + "ignoreTimeRange": false + }, + "units": { + "unit": "BYTES" + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + }, + { + "title": "Payload packets per Fluent Bit chunk", + "layout": { + "column": 9, + "row": 7, + "width": 4, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.line" + }, + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT min(logs.fb.payload.count) AS 'Minimum', average(logs.fb.payload.count) AS 'Average', max(logs.fb.payload.count) AS 'Maximum' FROM Metric timeseries MAX " + } + ], + "platformOptions": { + "ignoreTimeRange": false + }, + "units": { + "unit": "COUNT" + }, + "yAxisLeft": { + "zero": true + }, + "yAxisRight": { + "zero": true + } + } + } + ] + } + ], + "variables": [ + { + "name": "cluster_name", + "items": null, + "defaultValues": [ + { + "value": { + "string": "*" + } + } + ], + "nrqlQuery": { + "accountIds": [ + YOUR_ACCOUNT_ID + ], + "query": "SELECT uniques(cluster_name) FROM Metric where metricName = 'fluentbit_input_storage_overlimit'" + }, + "options": { + "ignoreTimeRange": false + }, + "title": "Cluster Name", + "type": "NRQL", + "isMultiSelection": true, + "replacementStrategy": "STRING" + } + ] +} \ No newline at end of file diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/NOTES.txt b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/NOTES.txt new file mode 100644 index 0000000000..289f2157f3 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/NOTES.txt @@ -0,0 +1,18 @@ +{{- if (include "newrelic-logging.areValuesValid" .) }} +Your deployment of the New Relic Kubernetes Logging is complete. You can check on the progress of this by running the following command: + + kubectl get daemonset -o wide -w --namespace {{ .Release.Namespace }} {{ template "newrelic-logging.fullname" . }} +{{- else -}} +############################################################################## +#### ERROR: You did not set a license key. #### +############################################################################## + +This deployment will be incomplete until you get your API key from New Relic. + +Then run: + + helm upgrade {{ .Release.Name }} \ + --set licenseKey=(your-license-key) \ + newrelic/newrelic-logging + +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/_helpers.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/_helpers.tpl new file mode 100644 index 0000000000..439d25cae4 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/_helpers.tpl @@ -0,0 +1,215 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "newrelic-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). +*/}} +{{- define "newrelic-logging.fullname" -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if ne $name .Release.Name -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s" $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} + + +{{/* Generate basic labels */}} +{{- define "newrelic-logging.labels" }} +app: {{ template "newrelic-logging.name" . }} +chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} +heritage: {{.Release.Service }} +release: {{.Release.Name }} +app.kubernetes.io/name: {{ template "newrelic-logging.name" . }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "newrelic-logging.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + + +{{/* +Create the name of the fluent bit config +*/}} +{{- define "newrelic-logging.fluentBitConfig" -}} +{{ template "newrelic-logging.fullname" . }}-fluent-bit-config +{{- end -}} + +{{/* +Return the licenseKey +*/}} +{{- define "newrelic-logging.licenseKey" -}} +{{- if .Values.global}} + {{- if .Values.global.licenseKey }} + {{- .Values.global.licenseKey -}} + {{- else -}} + {{- .Values.licenseKey | default "" -}} + {{- end -}} +{{- else -}} + {{- .Values.licenseKey | default "" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the cluster name +*/}} +{{- define "newrelic-logging.cluster" -}} +{{- if .Values.global}} + {{- if .Values.global.cluster }} + {{- .Values.global.cluster -}} + {{- else -}} + {{- .Values.cluster | default "" -}} + {{- end -}} +{{- else -}} + {{- .Values.cluster | default "" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the customSecretName +*/}} +{{- define "newrelic-logging.customSecretName" -}} +{{- if .Values.global }} + {{- if .Values.global.customSecretName }} + {{- .Values.global.customSecretName -}} + {{- else -}} + {{- .Values.customSecretName | default "" -}} + {{- end -}} +{{- else -}} + {{- .Values.customSecretName | default "" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the customSecretLicenseKey +*/}} +{{- define "newrelic-logging.customSecretKey" -}} +{{- if .Values.global }} + {{- if .Values.global.customSecretLicenseKey }} + {{- .Values.global.customSecretLicenseKey -}} + {{- else -}} + {{- if .Values.global.customSecretKey }} + {{- .Values.global.customSecretKey -}} + {{- else -}} + {{- .Values.customSecretKey | default "" -}} + {{- end -}} + {{- end -}} +{{- else -}} + {{- if .Values.customSecretLicenseKey }} + {{- .Values.customSecretLicenseKey -}} + {{- else -}} + {{- .Values.customSecretKey | default "" -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Returns nrStaging +*/}} +{{- define "newrelic.nrStaging" -}} +{{- if .Values.global }} + {{- if .Values.global.nrStaging }} + {{- .Values.global.nrStaging -}} + {{- end -}} +{{- else if .Values.nrStaging }} + {{- .Values.nrStaging -}} +{{- end -}} +{{- end -}} + +{{/* +Returns fargate +*/}} +{{- define "newrelic.fargate" -}} +{{- if .Values.global }} + {{- if .Values.global.fargate }} + {{- .Values.global.fargate -}} + {{- end -}} +{{- else if .Values.fargate }} + {{- .Values.fargate -}} +{{- end -}} +{{- end -}} + +{{/* +Returns lowDataMode +*/}} +{{- define "newrelic-logging.lowDataMode" -}} +{{/* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */}} +{{- if (get .Values "lowDataMode" | kindIs "bool") -}} + {{- if .Values.lowDataMode -}} + {{/* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic-logging.lowDataMode" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */}} + {{- .Values.lowDataMode -}} + {{- end -}} +{{- else -}} +{{/* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "lowDataMode" | kindIs "bool" -}} + {{- if $global.lowDataMode -}} + {{- $global.lowDataMode -}} + {{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Returns logsEndpoint +*/}} +{{- define "newrelic-logging.logsEndpoint" -}} +{{- if (include "newrelic.nrStaging" .) -}} +https://staging-log-api.newrelic.com/log/v1 +{{- else if .Values.endpoint -}} +{{ .Values.endpoint -}} +{{- else if eq (substr 0 2 (include "newrelic-logging.licenseKey" .)) "eu" -}} +https://log-api.eu.newrelic.com/log/v1 +{{- else -}} +https://log-api.newrelic.com/log/v1 +{{- end -}} +{{- end -}} + +{{/* +Returns metricsHost +*/}} +{{- define "newrelic-logging.metricsHost" -}} +{{- if (include "newrelic.nrStaging" .) -}} +staging-metric-api.newrelic.com +{{- else if eq (substr 0 2 (include "newrelic-logging.licenseKey" .)) "eu" -}} +metric-api.eu.newrelic.com +{{- else -}} +metric-api.newrelic.com +{{- end -}} +{{- end -}} + +{{/* +Returns if the template should render, it checks if the required values are set. +*/}} +{{- define "newrelic-logging.areValuesValid" -}} +{{- $licenseKey := include "newrelic-logging.licenseKey" . -}} +{{- $customSecretName := include "newrelic-logging.customSecretName" . -}} +{{- $customSecretKey := include "newrelic-logging.customSecretKey" . -}} +{{- and (or $licenseKey (and $customSecretName $customSecretKey))}} +{{- end -}} + +{{/* +If additionalEnvVariables is set, renames to extraEnv. Returns extraEnv. +*/}} +{{- define "newrelic-logging.extraEnv" -}} +{{- if .Values.fluentBit }} + {{- if .Values.fluentBit.additionalEnvVariables }} + {{- toYaml .Values.fluentBit.additionalEnvVariables -}} + {{- else if .Values.fluentBit.extraEnv }} + {{- toYaml .Values.fluentBit.extraEnv -}} + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/clusterrole.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/clusterrole.yaml new file mode 100644 index 0000000000..b36340fe63 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/clusterrole.yaml @@ -0,0 +1,23 @@ +{{- if .Values.rbac.create }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: {{ include "newrelic-logging.labels" . | indent 4 }} + name: {{ template "newrelic-logging.fullname" . }} +rules: + - apiGroups: [""] + resources: + - namespaces + - pods + verbs: ["get", "list", "watch"] +{{- if .Values.rbac.pspEnabled }} + - apiGroups: + - extensions + resources: + - podsecuritypolicies + resourceNames: + - privileged-{{ template "newrelic-logging.fullname" . }} + verbs: + - use +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/clusterrolebinding.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/clusterrolebinding.yaml new file mode 100644 index 0000000000..6b258f6973 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/clusterrolebinding.yaml @@ -0,0 +1,15 @@ +{{- if .Values.rbac.create }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: {{ include "newrelic-logging.labels" . | indent 4 }} + name: {{ template "newrelic-logging.fullname" . }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "newrelic-logging.fullname" . }} +subjects: +- kind: ServiceAccount + name: {{ include "newrelic.common.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/configmap.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/configmap.yaml new file mode 100644 index 0000000000..4b1d890144 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/configmap.yaml @@ -0,0 +1,38 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + namespace: {{ .Release.Namespace }} + labels: {{ include "newrelic-logging.labels" . | indent 4 }} + name: {{ template "newrelic-logging.fluentBitConfig" . }} +data: + fluent-bit.conf: | + {{- if .Values.fluentBit.config.service }} + {{- .Values.fluentBit.config.service | nindent 4 }} + {{- end }} + {{- if .Values.fluentBit.config.inputs }} + {{- .Values.fluentBit.config.inputs | nindent 4 }} + {{- end }} + {{- if .Values.fluentBit.config.extraInputs }} + {{- .Values.fluentBit.config.extraInputs | nindent 4}} + {{- end }} + {{- if and (include "newrelic-logging.lowDataMode" .) (.Values.fluentBit.config.lowDataModeFilters) }} + {{- .Values.fluentBit.config.lowDataModeFilters | nindent 4 }} + {{- else }} + {{- .Values.fluentBit.config.filters | nindent 4 }} + {{- end }} + {{- if .Values.fluentBit.config.extraFilters }} + {{- .Values.fluentBit.config.extraFilters | nindent 4}} + {{- end }} + {{- if .Values.fluentBit.config.outputs }} + {{- .Values.fluentBit.config.outputs | nindent 4 }} + {{- end }} + {{- if .Values.fluentBit.config.extraOutputs }} + {{- .Values.fluentBit.config.extraOutputs | nindent 4}} + {{- end }} + {{- if and (.Values.fluentBit.sendMetrics) (.Values.fluentBit.config.metricInstrumentation) }} + {{- .Values.fluentBit.config.metricInstrumentation | nindent 4}} + {{- end }} + parsers.conf: | + {{- if .Values.fluentBit.config.parsers }} + {{- .Values.fluentBit.config.parsers | nindent 4}} + {{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/daemonset-windows.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/daemonset-windows.yaml new file mode 100644 index 0000000000..6a3145d134 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/daemonset-windows.yaml @@ -0,0 +1,174 @@ +{{- if and (include "newrelic-logging.areValuesValid" $) $.Values.enableWindows }} +{{- range .Values.windowsOsList }} +apiVersion: apps/v1 +kind: DaemonSet +metadata: + namespace: {{ $.Release.Namespace }} + labels: + kubernetes.io/os: windows +{{ include "newrelic-logging.labels" $ | indent 4 }} + name: {{ template "newrelic-logging.fullname" $ }}-windows-{{ .version }} + annotations: + {{- if $.Values.daemonSet.annotations }} +{{ toYaml $.Values.daemonSet.annotations | indent 4 }} + {{- end }} +spec: + updateStrategy: + type: {{ $.Values.updateStrategy }} + selector: + matchLabels: + app: {{ template "newrelic-logging.name" $ }} + release: {{ $.Release.Name }} + kubernetes.io/os: windows + template: + metadata: + annotations: + checksum/fluent-bit-config: {{ include (print $.Template.BasePath "/configmap.yaml") $ | sha256sum }} + {{- if $.Values.podAnnotations }} +{{ toYaml $.Values.podAnnotations | indent 8}} + {{- end }} + labels: + app: {{ template "newrelic-logging.name" $ }} + release: {{ $.Release.Name }} + kubernetes.io/os: windows + app.kubernetes.io/name: {{ template "newrelic-logging.name" $ }} + {{- if $.Values.podLabels}} +{{ toYaml $.Values.podLabels | indent 8 }} + {{- end }} + spec: + serviceAccountName: {{ include "newrelic.common.serviceAccount.name" $ }} + {{- with include "newrelic.common.dnsConfig" $ }} + dnsConfig: + {{- . | nindent 8 }} + {{- end }} + dnsPolicy: ClusterFirst + terminationGracePeriodSeconds: 10 + {{- with include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" (list $.Values.image.pullSecrets) "context" $) }} + imagePullSecrets: + {{- . | nindent 8 }} + {{- end }} + {{- if $.Values.hostNetwork }} + hostNetwork: {{ $.Values.hostNetwork }} + {{- end }} + {{- if $.Values.windows.initContainers }} + initContainers: +{{ toYaml $.Values.windows.initContainers | indent 8 }} + {{- end }} + containers: + - name: {{ template "newrelic-logging.name" $ }} + # We have to use 'replace' to remove the double-quotes that "newrelic.common.images.image" has, so that + # we can append the Windows image tag suffix (and then re-quote that value) + image: "{{ include "newrelic.common.images.image" ( dict "imageRoot" $.Values.image "context" $) | replace "\"" ""}}-{{ .imageTagSuffix }}" + imagePullPolicy: "{{ $.Values.image.pullPolicy }}" + securityContext: {} + env: + - name: ENDPOINT + value: {{ include "newrelic-logging.logsEndpoint" $ | quote }} + - name: SOURCE + value: {{ if (include "newrelic-logging.lowDataMode" $) }} "k8s" {{- else }} "kubernetes" {{- end }} + - name: LICENSE_KEY + valueFrom: + secretKeyRef: + {{- if (include "newrelic-logging.licenseKey" $) }} + name: {{ template "newrelic-logging.fullname" $ }}-config + key: license + {{- else }} + name: {{ include "newrelic-logging.customSecretName" $ }} + key: {{ include "newrelic-logging.customSecretKey" $ }} + {{- end }} + - name: CLUSTER_NAME + value: {{ include "newrelic-logging.cluster" $ }} + - name: LOG_LEVEL + value: {{ $.Values.fluentBit.logLevel | quote }} + - name: LOG_PARSER + {{- if $.Values.fluentBit.criEnabled }} + value: "cri,docker" + {{- else }} + value: "docker,cri" + {{- end }} + {{- if or (not $.Values.fluentBit.persistence) (eq $.Values.fluentBit.persistence.mode "hostPath") }} + - name: FB_DB + value: {{ $.Values.fluentBit.windowsDb | quote }} + {{- else }} + - name: FB_DB + value: "" + {{- end }} + - name: PATH + value: {{ $.Values.fluentBit.windowsPath | quote }} + - name: K8S_BUFFER_SIZE + value: {{ $.Values.fluentBit.k8sBufferSize | quote }} + - name: K8S_LOGGING_EXCLUDE + value: {{ $.Values.fluentBit.k8sLoggingExclude | default "false" | quote }} + - name: LOW_DATA_MODE + value: {{ include "newrelic-logging.lowDataMode" $ | default "false" | quote }} + - name: RETRY_LIMIT + value: {{ $.Values.fluentBit.retryLimit | quote }} + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: HOSTNAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: SEND_OUTPUT_PLUGIN_METRICS + value: {{ $.Values.fluentBit.sendMetrics | default "false" | quote }} + - name: METRICS_HOST + value: {{ include "newrelic-logging.metricsHost" $ | quote }} + {{- include "newrelic-logging.extraEnv" $ | nindent 12 }} + command: + - C:\fluent-bit\bin\fluent-bit.exe + - -c + - c:\fluent-bit\etc\fluent-bit.conf + - -e + - C:\fluent-bit\bin\out_newrelic.dll + {{- if $.Values.exposedPorts }} + ports: {{ toYaml $.Values.exposedPorts | nindent 12 }} + {{- end }} + volumeMounts: + - mountPath: C:\fluent-bit\etc + name: fluent-bit-config + - mountPath: C:\var\log + name: logs + {{- if and ($.Values.fluentBit.persistence) (ne $.Values.fluentBit.persistence.mode "hostPath") }} + readOnly: true + {{- end }} + # We need to also mount this because the logs in C:\var\logs are actually symlinks to C:\ProgramData. + # So, in order to be able to read these logs, the reading process needs to also have access to C:\ProgramData. + - mountPath: C:\ProgramData + name: progdata + {{- if and ($.Values.fluentBit.persistence) (ne $.Values.fluentBit.persistence.mode "hostPath") }} + readOnly: true + {{- end }} + {{- if $.Values.resources }} + resources: +{{ toYaml $.Values.resources | indent 12 }} + {{- end }} + volumes: + - name: fluent-bit-config + configMap: + name: {{ template "newrelic-logging.fluentBitConfig" $ }} + - name: logs + hostPath: + path: C:\var\log + - name: progdata + hostPath: + path: C:\ProgramData + {{- if $.Values.priorityClassName }} + priorityClassName: {{ $.Values.priorityClassName }} + {{- end }} + nodeSelector: + {{- if $.Values.windowsNodeSelector }} +{{ toYaml $.Values.windowsNodeSelector | indent 8 }} + {{- else }} + kubernetes.io/os: windows + # Windows containers can only be executed on hosts running the exact same Windows version and build number + node.kubernetes.io/windows-build: {{ .buildNumber }} + {{- end }} + {{- if $.Values.tolerations }} + tolerations: +{{ toYaml $.Values.tolerations | indent 8 }} + {{- end }} +--- +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/daemonset.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/daemonset.yaml new file mode 100644 index 0000000000..7dac9e07e3 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/daemonset.yaml @@ -0,0 +1,211 @@ +{{- if and (include "newrelic-logging.areValuesValid" .) .Values.enableLinux }} +apiVersion: apps/v1 +kind: DaemonSet +metadata: + namespace: {{ .Release.Namespace }} + labels: {{ include "newrelic-logging.labels" . | indent 4 }} + name: {{ template "newrelic-logging.fullname" . }} + annotations: + {{- if .Values.daemonSet.annotations }} +{{ toYaml .Values.daemonSet.annotations | indent 4 }} + {{- end }} +spec: + updateStrategy: + type: {{ .Values.updateStrategy }} + selector: + matchLabels: + app: {{ template "newrelic-logging.name" . }} + release: {{.Release.Name }} + template: + metadata: + annotations: + checksum/fluent-bit-config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + {{- if .Values.podAnnotations }} +{{ toYaml .Values.podAnnotations | indent 8}} + {{- end }} + labels: + app: {{ template "newrelic-logging.name" . }} + release: {{.Release.Name }} + kubernetes.io/os: linux + app.kubernetes.io/name: {{ template "newrelic-logging.name" . }} + {{- if .Values.podLabels}} +{{ toYaml .Values.podLabels | indent 8 }} + {{- end }} + spec: + serviceAccountName: {{ include "newrelic.common.serviceAccount.name" . }} + {{- with include "newrelic.common.dnsConfig" . }} + dnsConfig: + {{- . | nindent 8 }} + {{- end }} + dnsPolicy: ClusterFirstWithHostNet + terminationGracePeriodSeconds: 10 + {{- with include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" (list .Values.image.pullSecrets) "context" .) }} + imagePullSecrets: + {{- . | nindent 8 }} + {{- end }} + {{- with include "newrelic.common.securityContext.pod" . }} + securityContext: + {{- . | nindent 8 }} + {{- end }} + {{- if .Values.hostNetwork }} + hostNetwork: {{ .Values.hostNetwork }} + {{- end }} + initContainers: + {{- if and (.Values.fluentBit.persistence) (eq .Values.fluentBit.persistence.mode "persistentVolume") }} + - name: init + image: busybox:1.36 + command: ["/bin/sh", "-c"] + args: ["/bin/find /db -type f -mtime +1 -delete"] # Delete all db files not updated in the last 24h + volumeMounts: + - name: fb-db-pvc + mountPath: /db + {{- end }} + {{- if .Values.initContainers }} +{{ toYaml .Values.initContainers | indent 8 }} + {{- end }} + containers: + - name: {{ template "newrelic-logging.name" . }} + {{- with include "newrelic.common.securityContext.container" . }} + securityContext: + {{- . | nindent 12 }} + {{- end }} + image: {{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.image "context" .) }} + imagePullPolicy: "{{ .Values.image.pullPolicy }}" + env: + - name: ENDPOINT + value: {{ include "newrelic-logging.logsEndpoint" . | quote }} + - name: SOURCE + value: {{ if (include "newrelic-logging.lowDataMode" .) }} "k8s" {{- else }} "kubernetes" {{- end }} + - name: LICENSE_KEY + valueFrom: + secretKeyRef: + {{- if (include "newrelic-logging.licenseKey" .) }} + name: {{ template "newrelic-logging.fullname" . }}-config + key: license + {{- else }} + name: {{ include "newrelic-logging.customSecretName" . }} + key: {{ include "newrelic-logging.customSecretKey" . }} + {{- end }} + - name: CLUSTER_NAME + value: {{ include "newrelic-logging.cluster" . }} + - name: LOG_LEVEL + value: {{ .Values.fluentBit.logLevel | quote }} + - name: LOG_PARSER + {{- if .Values.fluentBit.criEnabled }} + value: "cri,docker" + {{- else }} + value: "docker,cri" + {{- end }} + {{- if or (not .Values.fluentBit.persistence) (eq .Values.fluentBit.persistence.mode "hostPath") }} + - name: FB_DB + value: {{ .Values.fluentBit.db | quote }} + {{- else if eq .Values.fluentBit.persistence.mode "persistentVolume" }} + - name: FB_DB + value: "/db/$(NODE_NAME)-fb.db" + {{- else }} + - name: FB_DB + value: "" + {{- end }} + - name: PATH + value: {{ .Values.fluentBit.path | quote }} + - name: K8S_BUFFER_SIZE + value: {{ .Values.fluentBit.k8sBufferSize | quote }} + - name: K8S_LOGGING_EXCLUDE + value: {{ .Values.fluentBit.k8sLoggingExclude | default "false" | quote }} + - name: LOW_DATA_MODE + value: {{ include "newrelic-logging.lowDataMode" . | default "false" | quote }} + - name: RETRY_LIMIT + value: {{ .Values.fluentBit.retryLimit | quote }} + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: HOSTNAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: SEND_OUTPUT_PLUGIN_METRICS + value: {{ $.Values.fluentBit.sendMetrics | default "false" | quote }} + - name: METRICS_HOST + value: {{ include "newrelic-logging.metricsHost" . | quote }} + {{- include "newrelic-logging.extraEnv" . | nindent 12 }} + command: + - /fluent-bit/bin/fluent-bit + - -c + - /fluent-bit/etc/fluent-bit.conf + - -e + - /fluent-bit/bin/out_newrelic.so + volumeMounts: + - name: fluent-bit-config + mountPath: /fluent-bit/etc + - name: logs + # We mount /var by default because container logs could be on /var/log or /var/lib/docker/containers (symlinked to /var/log) + mountPath: {{ .Values.fluentBit.linuxMountPath | default "/var" }} + {{- if and (.Values.fluentBit.persistence) (ne .Values.fluentBit.persistence.mode "hostPath") }} + readOnly: true + {{- end }} + {{- if and (.Values.fluentBit.persistence) (eq .Values.fluentBit.persistence.mode "persistentVolume") }} + - name: fb-db-pvc + mountPath: /db + {{- end }} + {{- if .Values.extraVolumeMounts }} + {{- toYaml .Values.extraVolumeMounts | nindent 12 }} + {{- end }} + {{- if .Values.exposedPorts }} + ports: {{ toYaml .Values.exposedPorts | nindent 12 }} + {{- end }} + {{- if .Values.resources }} + resources: +{{ toYaml .Values.resources | indent 12 }} + {{- end }} + volumes: + - name: fluent-bit-config + configMap: + name: {{ template "newrelic-logging.fluentBitConfig" . }} + - name: logs + hostPath: + path: {{ .Values.fluentBit.linuxMountPath | default "/var" }} + {{- if and (.Values.fluentBit.persistence) (eq .Values.fluentBit.persistence.mode "persistentVolume") }} + - name: fb-db-pvc + persistentVolumeClaim: + {{- if .Values.fluentBit.persistence.persistentVolume.existingVolumeClaim }} + claimName: {{ .Values.fluentBit.persistence.persistentVolume.existingVolumeClaim }} + {{- else }} + claimName: {{ template "newrelic-logging.fullname" . }}-pvc + {{- end }} + {{- end }} + {{- if .Values.extraVolumes }} + {{- toYaml .Values.extraVolumes | nindent 8 }} + {{- end }} + {{- if $.Values.priorityClassName }} + priorityClassName: {{ $.Values.priorityClassName }} + {{- end }} + {{- if .Values.nodeAffinity }} + affinity: + nodeAffinity: {{ .Values.nodeAffinity | toYaml | nindent 10 }} + {{- else if include "newrelic.fargate" . }} + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: eks.amazonaws.com/compute-type + operator: NotIn + values: + - fargate + {{- end }} + nodeSelector: + {{- if .Values.nodeSelector }} +{{ toYaml .Values.nodeSelector | indent 8 }} + {{- else if $.Values.enableWindows }} + # We add this only if Windows is enabled to keep backwards-compatibility. Prior to version 1.14, this label was + # named beta.kubernetes.io/os. In version 1.14, it was deprecated and replaced by this one. Version 1.14 also + # introduces Windows support. Therefore, anyone wishing to use Windows containers must bet at version >=1.14 and + # are going to need this label, in order to avoid placing a linux container on a windows node, and vice-versa. + kubernetes.io/os: linux + {{- end }} + {{- if .Values.tolerations }} + tolerations: +{{ toYaml .Values.tolerations | indent 8 }} + {{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/persistentvolume.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/persistentvolume.yaml new file mode 100644 index 0000000000..f2fb93d77a --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/persistentvolume.yaml @@ -0,0 +1,57 @@ +{{- if (not (empty .Values.fluentBit.persistence)) }} + +{{- if and (eq .Values.fluentBit.persistence.mode "persistentVolume") (not .Values.fluentBit.persistence.persistentVolume.storageClass) (not .Values.fluentBit.persistence.persistentVolume.existingVolumeClaim) }} +{{ fail "You should provide a ReadWriteMany storageClass or an existingVolumeClaim if using persitentVolume as Fluent Bit persistence mode." }} +{{- end }} + +{{- if and (eq .Values.fluentBit.persistence.mode "persistentVolume") (not .Values.fluentBit.persistence.persistentVolume.existingVolumeClaim) }} +{{- if and (not .Values.fluentBit.persistence.persistentVolume.dynamicProvisioning) (not .Values.fluentBit.persistence.persistentVolume.existingVolume) }} +apiVersion: v1 +kind: PersistentVolume +metadata: + namespace: {{ .Release.Namespace }} + labels: {{ include "newrelic-logging.labels" . | indent 4 }} + name: {{ template "newrelic-logging.fullname" . }}-pv + annotations: + {{- if .Values.fluentBit.persistence.persistentVolume.annotations.volume }} +{{ toYaml .Values.fluentBit.persistence.persistentVolume.annotations.volume | indent 4 }} + {{- end }} +spec: + accessModes: + - ReadWriteMany + capacity: + storage: {{ .Values.fluentBit.persistence.persistentVolume.size }} + storageClassName: {{ .Values.fluentBit.persistence.persistentVolume.storageClass }} + persistentVolumeReclaimPolicy: Delete + {{- if .Values.fluentBit.persistence.persistentVolume.extra.volume }} +{{ toYaml .Values.fluentBit.persistence.persistentVolume.extra.volume | indent 2 }} + {{- end }} +--- +{{- end }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + namespace: {{ .Release.Namespace }} + labels: {{ include "newrelic-logging.labels" . | indent 4 }} + name: {{ template "newrelic-logging.fullname" . }}-pvc + annotations: + {{- if .Values.fluentBit.persistence.persistentVolume.annotations.claim }} +{{ toYaml .Values.fluentBit.persistence.persistentVolume.annotations.claim | indent 4 }} + {{- end }} +spec: + storageClassName: {{ .Values.fluentBit.persistence.persistentVolume.storageClass }} + accessModes: + - ReadWriteMany +{{- if .Values.fluentBit.persistence.persistentVolume.existingVolume }} + volumeName: {{ .Values.fluentBit.persistence.persistentVolume.existingVolume }} +{{- else if not .Values.fluentBit.persistence.persistentVolume.dynamicProvisioning }} + volumeName: {{ template "newrelic-logging.fullname" . }}-pv +{{- end }} + resources: + requests: + storage: {{ .Values.fluentBit.persistence.persistentVolume.size }} + {{- if .Values.fluentBit.persistence.persistentVolume.extra.claim }} +{{ toYaml .Values.fluentBit.persistence.persistentVolume.extra.claim | indent 2 }} + {{- end }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/podsecuritypolicy.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/podsecuritypolicy.yaml new file mode 100644 index 0000000000..2c8c598e24 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/podsecuritypolicy.yaml @@ -0,0 +1,24 @@ +{{- if .Values.rbac.pspEnabled }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: privileged-{{ template "newrelic-logging.fullname" . }} +spec: + allowedCapabilities: + - '*' + fsGroup: + rule: RunAsAny + runAsUser: + rule: RunAsAny + seLinux: + rule: RunAsAny + supplementalGroups: + rule: RunAsAny + volumes: + - '*' + hostPID: true + hostIPC: true + hostPorts: + - min: 1 + max: 65536 +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/secret.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/secret.yaml new file mode 100644 index 0000000000..47a56e573e --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/secret.yaml @@ -0,0 +1,12 @@ +{{- $licenseKey := include "newrelic-logging.licenseKey" . -}} +{{- if $licenseKey }} +apiVersion: v1 +kind: Secret +metadata: + namespace: {{ .Release.Namespace }} + labels: {{ include "newrelic-logging.labels" . | indent 4 }} + name: {{ template "newrelic-logging.fullname" . }}-config +type: Opaque +data: + license: {{ $licenseKey | b64enc }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/serviceaccount.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/serviceaccount.yaml new file mode 100644 index 0000000000..51da56a3e7 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/templates/serviceaccount.yaml @@ -0,0 +1,17 @@ +{{- if include "newrelic.common.serviceAccount.create" . -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + {{- if include "newrelic.common.serviceAccount.annotations" . }} + annotations: + {{- include "newrelic.common.serviceAccount.annotations" . | nindent 4 }} + {{- end }} + labels: + app: {{ template "newrelic-logging.name" . }} + chart: {{ template "newrelic-logging.chart" . }} + heritage: "{{ .Release.Service }}" + release: "{{ .Release.Name }}" + {{- /*include "newrelic.common.labels" . | nindent 4 /!\ Breaking change /!\ */}} + name: {{ include "newrelic.common.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/cri_parser_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/cri_parser_test.yaml new file mode 100644 index 0000000000..f4a1d01d07 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/cri_parser_test.yaml @@ -0,0 +1,37 @@ +suite: test cri, docker parser options in daemonsets +templates: + - templates/configmap.yaml + - templates/daemonset.yaml + - templates/daemonset-windows.yaml +release: + name: my-release + namespace: my-namespace +tests: + - it: cri enabled by default and docker as fallback + templates: + - templates/daemonset.yaml + - templates/daemonset-windows.yaml + set: + licenseKey: nr_license_key + enableWindows: true + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: LOG_PARSER + value: "cri,docker" + - it: docker is set if enabled by and cri as fallback + templates: + - templates/daemonset.yaml + - templates/daemonset-windows.yaml + set: + licenseKey: nr_license_key + enableWindows: true + fluentBit: + criEnabled: false + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: LOG_PARSER + value: "docker,cri" \ No newline at end of file diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/dns_config_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/dns_config_test.yaml new file mode 100644 index 0000000000..76d24eac5f --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/dns_config_test.yaml @@ -0,0 +1,62 @@ +suite: test dnsConfig options in daemonsets +templates: + - templates/configmap.yaml + - templates/daemonset.yaml + - templates/daemonset-windows.yaml +release: + name: my-release + namespace: my-namespace +tests: + - it: daemonsets contain dnsConfig block when provided + set: + licenseKey: nr_license_key + enableWindows: true + dnsConfig: + nameservers: + - 192.0.2.1 + asserts: + - exists: + path: spec.template.spec.dnsConfig + template: templates/daemonset.yaml + - exists: + path: spec.template.spec.dnsConfig + template: templates/daemonset-windows.yaml + + - it: daemonsets do not contain dnsConfig block when not provided + set: + licenseKey: nr_license_key + enableWindows: true + dnsConfig: {} + asserts: + - notExists: + path: spec.template.spec.dnsConfig + template: templates/daemonset.yaml + - notExists: + path: spec.template.spec.dnsConfig + template: templates/daemonset-windows.yaml + + - it: daemonsets contain provided dnsConfig options + set: + licenseKey: nr_license_key + enableWindows: true + dnsConfig: + options: + - name: ndots + value: "1" + asserts: + - equal: + path: spec.template.spec.dnsConfig.options[0].name + value: ndots + template: templates/daemonset.yaml + - equal: + path: spec.template.spec.dnsConfig.options[0].value + value: "1" + template: templates/daemonset.yaml + - equal: + path: spec.template.spec.dnsConfig.options[0].name + value: ndots + template: templates/daemonset-windows.yaml + - equal: + path: spec.template.spec.dnsConfig.options[0].value + value: "1" + template: templates/daemonset-windows.yaml diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/endpoint_region_selection_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/endpoint_region_selection_test.yaml new file mode 100644 index 0000000000..82e700d930 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/endpoint_region_selection_test.yaml @@ -0,0 +1,128 @@ +suite: test endpoint selection based on region settings +templates: + - templates/configmap.yaml + - templates/daemonset.yaml + - templates/daemonset-windows.yaml +release: + name: endpoint-selection-release + namespace: endpoint-selection-namespace +tests: + + - it: selects staging endpoints if nrStaging is enabled + set: + licenseKey: nr_license_key + nrStaging: true + enableWindows: true + asserts: + # Linux + - contains: + path: spec.template.spec.containers[0].env + content: + name: ENDPOINT + value: "https://staging-log-api.newrelic.com/log/v1" + template: templates/daemonset.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: METRICS_HOST + value: "staging-metric-api.newrelic.com" + template: templates/daemonset.yaml + # Windows + - contains: + path: spec.template.spec.containers[0].env + content: + name: ENDPOINT + value: "https://staging-log-api.newrelic.com/log/v1" + template: templates/daemonset-windows.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: METRICS_HOST + value: "staging-metric-api.newrelic.com" + template: templates/daemonset-windows.yaml + + - it: selects US endpoints for a US license key + set: + licenseKey: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaFFFFNRAL + enableWindows: true + asserts: + # Linux + - contains: + path: spec.template.spec.containers[0].env + content: + name: ENDPOINT + value: "https://log-api.newrelic.com/log/v1" + template: templates/daemonset.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: METRICS_HOST + value: "metric-api.newrelic.com" + template: templates/daemonset.yaml + # Windows + - contains: + path: spec.template.spec.containers[0].env + content: + name: ENDPOINT + value: "https://log-api.newrelic.com/log/v1" + template: templates/daemonset-windows.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: METRICS_HOST + value: "metric-api.newrelic.com" + template: templates/daemonset-windows.yaml + + - it: selects EU endpoints for a EU license key + set: + licenseKey: euaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaFFFFNRAL + enableWindows: true + asserts: + # Linux + - contains: + path: spec.template.spec.containers[0].env + content: + name: ENDPOINT + value: "https://log-api.eu.newrelic.com/log/v1" + template: templates/daemonset.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: METRICS_HOST + value: "metric-api.eu.newrelic.com" + template: templates/daemonset.yaml + # Windows + - contains: + path: spec.template.spec.containers[0].env + content: + name: ENDPOINT + value: "https://log-api.eu.newrelic.com/log/v1" + template: templates/daemonset-windows.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: METRICS_HOST + value: "metric-api.eu.newrelic.com" + template: templates/daemonset-windows.yaml + + + - it: selects custom logs endpoint if provided + set: + licenseKey: euaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaFFFFNRAL + endpoint: custom + enableWindows: true + asserts: + # Linux + - contains: + path: spec.template.spec.containers[0].env + content: + name: ENDPOINT + value: "custom" + template: templates/daemonset.yaml + # Windows + - contains: + path: spec.template.spec.containers[0].env + content: + name: ENDPOINT + value: "custom" + template: templates/daemonset-windows.yaml \ No newline at end of file diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/fluentbit_k8logging_exclude_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/fluentbit_k8logging_exclude_test.yaml new file mode 100644 index 0000000000..446f829b00 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/fluentbit_k8logging_exclude_test.yaml @@ -0,0 +1,45 @@ +suite: test fluent-bit exclude logging +templates: + - templates/daemonset.yaml + - templates/configmap.yaml + - templates/persistentvolume.yaml +release: + name: my-release + namespace: my-namespace +tests: + - it: K8S_LOGGING_EXCLUDE set true + templates: + - templates/daemonset.yaml + set: + licenseKey: nr_license_key + fluentBit: + k8sLoggingExclude: true + asserts: + - equal: + path: spec.template.spec.containers[0].env[?(@.name=="K8S_LOGGING_EXCLUDE")].value + value: "true" + template: templates/daemonset.yaml + - it: K8S_LOGGING_EXCLUDE set false + templates: + - templates/daemonset.yaml + set: + licenseKey: nr_license_key + fluentBit: + k8sLoggingExclude: false + asserts: + - equal: + path: spec.template.spec.containers[0].env[?(@.name=="K8S_LOGGING_EXCLUDE")].value + value: "false" + template: templates/daemonset.yaml + - it: K8S_LOGGING_EXCLUDE set value xyz and expect it to be set + templates: + - templates/daemonset.yaml + set: + licenseKey: nr_license_key + fluentBit: + k8sLoggingExclude: xyz + asserts: + - equal: + path: spec.template.spec.containers[0].env[?(@.name=="K8S_LOGGING_EXCLUDE")].value + value: "xyz" + template: templates/daemonset.yaml diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/fluentbit_persistence_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/fluentbit_persistence_test.yaml new file mode 100644 index 0000000000..67d14c7955 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/fluentbit_persistence_test.yaml @@ -0,0 +1,317 @@ +suite: test fluent-bit persistence options +templates: + - templates/daemonset.yaml + - templates/configmap.yaml + - templates/persistentvolume.yaml +release: + name: my-release + namespace: my-namespace +tests: + - it: default persistence is hostPath, DB is set properly and logs volume is read/write + set: + licenseKey: nr_license_key + asserts: + - contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: logs + mountPath: /var + template: templates/daemonset.yaml + - notContains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: fb-db-pvc + mountPath: /db + template: templates/daemonset.yaml + - contains: + path: spec.template.spec.volumes + content: + name: logs + hostPath: + path: /var + template: templates/daemonset.yaml + - notContains: + path: spec.template.spec.volumes + content: + name: fb-db-pvc + persistentVolumeClaim: + claimName: my-release-newrelic-logging-pvc + template: templates/daemonset.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: FB_DB + value: /var/log/flb_kube.db + template: templates/daemonset.yaml + - hasDocuments: + count: 0 + template: templates/persistentvolume.yaml + - it: fluentBit.persistence set to none should keep FB_DB env empty and mount logs volume as read-only + set: + licenseKey: nr_license_key + fluentBit: + persistence: + mode: none + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: FB_DB + value: "" + template: templates/daemonset.yaml + - contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: logs + mountPath: /var + readOnly: true + template: templates/daemonset.yaml + - notContains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: fb-db-pvc + mountPath: /db + template: templates/daemonset.yaml + - notContains: + path: spec.template.spec.volumes + content: + name: fb-db-pvc + persistentVolumeClaim: + claimName: my-release-newrelic-logging-pvc + template: templates/daemonset.yaml + - hasDocuments: + count: 0 + template: templates/persistentvolume.yaml + - it: fluentBit.persistence set to persistentVolume should create volume, add it to daemonset, add an initContainer to cleanup and set the FB_DB. Dynamic provisioning is enabled by default. + set: + licenseKey: nr_license_key + fluentBit: + persistence: + mode: persistentVolume + persistentVolume: + storageClass: sample-rwx + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: FB_DB + value: "/db/$(NODE_NAME)-fb.db" + template: templates/daemonset.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + template: templates/daemonset.yaml + - contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: logs + mountPath: /var + readOnly: true + template: templates/daemonset.yaml + - contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: fb-db-pvc + mountPath: /db + template: templates/daemonset.yaml + - contains: + path: spec.template.spec.volumes + content: + name: fb-db-pvc + persistentVolumeClaim: + claimName: my-release-newrelic-logging-pvc + template: templates/daemonset.yaml + - isNotNullOrEmpty: + path: spec.template.spec.initContainers + template: templates/daemonset.yaml + - contains: + path: spec.template.spec.initContainers[0].volumeMounts + content: + name: fb-db-pvc + mountPath: /db + template: templates/daemonset.yaml + - hasDocuments: + count: 1 + template: templates/persistentvolume.yaml + - isKind: + of: PersistentVolumeClaim + template: templates/persistentvolume.yaml + - equal: + path: spec.accessModes + value: + - ReadWriteMany + template: templates/persistentvolume.yaml + - it: fluentBit.persistence.persistentVolume with non dynamic provisioning should create the PV and PVC + set: + licenseKey: nr_license_key + fluentBit: + persistence: + mode: persistentVolume + persistentVolume: + storageClass: sample-rwx + dynamicProvisioning: false + asserts: + - hasDocuments: + count: 2 + template: templates/persistentvolume.yaml + - isKind: + of: PersistentVolume + documentIndex: 0 + template: templates/persistentvolume.yaml + - isKind: + of: PersistentVolumeClaim + documentIndex: 1 + template: templates/persistentvolume.yaml + - equal: + path: spec.accessModes + value: + - ReadWriteMany + documentIndex: 0 + template: templates/persistentvolume.yaml + - equal: + path: spec.accessModes + value: + - ReadWriteMany + documentIndex: 1 + template: templates/persistentvolume.yaml + - it: fluentBit.persistence storage class should be set properly on PV and PVC + set: + licenseKey: nr_license_key + fluentBit: + persistence: + mode: persistentVolume + persistentVolume: + dynamicProvisioning: false + storageClass: sample-storage-rwx + asserts: + - equal: + path: spec.storageClassName + value: sample-storage-rwx + documentIndex: 0 + template: templates/persistentvolume.yaml + - equal: + path: spec.storageClassName + value: sample-storage-rwx + documentIndex: 1 + template: templates/persistentvolume.yaml + - it: fluentBit.persistence.persistentVolume size should be set properly on PV and PVC + set: + licenseKey: nr_license_key + fluentBit: + persistence: + mode: persistentVolume + persistentVolume: + storageClass: sample-rwx + dynamicProvisioning: false + size: 100Gi + asserts: + - equal: + path: spec.capacity.storage + value: 100Gi + documentIndex: 0 + template: templates/persistentvolume.yaml + - equal: + path: spec.resources.requests.storage + value: 100Gi + documentIndex: 1 + template: templates/persistentvolume.yaml + - it: fluentBit.persistence.persistentVolume not dynamic provisioned but volumeName provided should use the volumeName and do not create a PV + set: + licenseKey: nr_license_key + fluentBit: + persistence: + mode: persistentVolume + persistentVolume: + storageClass: sample-rwx + dynamicProvisioning: false + existingVolume: existing-volume + asserts: + - hasDocuments: + count: 1 + template: templates/persistentvolume.yaml + - isKind: + of: PersistentVolumeClaim + template: templates/persistentvolume.yaml + - equal: + path: spec.volumeName + value: existing-volume + template: templates/persistentvolume.yaml + - it: fluentBit.persistence.persistentVolume if a existing claim is provided it's used and PV/PVC are not created + set: + licenseKey: nr_license_key + fluentBit: + persistence: + mode: persistentVolume + persistentVolume: + storageClass: sample-rwx + dynamicProvisioning: false + existingVolumeClaim: existing-claim + asserts: + - hasDocuments: + count: 0 + template: templates/persistentvolume.yaml + - contains: + path: spec.template.spec.volumes + content: + name: fb-db-pvc + persistentVolumeClaim: + claimName: existing-claim + template: templates/daemonset.yaml + - it: fluentBit.persistence.persistentVolume annotations for PV and PVC are used + set: + licenseKey: nr_license_key + fluentBit: + persistence: + mode: persistentVolume + persistentVolume: + storageClass: sample-rwx + annotations: + volume: + foo: bar + claim: + baz: qux + dynamicProvisioning: false + asserts: + - equal: + path: metadata.annotations.foo + value: bar + documentIndex: 0 + template: templates/persistentvolume.yaml + - equal: + path: metadata.annotations.baz + value: qux + documentIndex: 1 + template: templates/persistentvolume.yaml + - it: fluentBit.persistence.persistentVolume extra for PV and PVC are used + set: + licenseKey: nr_license_key + fluentBit: + persistence: + mode: persistentVolume + persistentVolume: + storageClass: sample-rwx + extra: + volume: + nfs: + path: /tmp/ + server: 1.1.1.1 + claim: + some: property + dynamicProvisioning: false + asserts: + - equal: + path: spec.nfs + value: + path: /tmp/ + server: 1.1.1.1 + documentIndex: 0 + template: templates/persistentvolume.yaml + - equal: + path: spec.some + value: property + documentIndex: 1 + template: templates/persistentvolume.yaml diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/fluentbit_pod_label_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/fluentbit_pod_label_test.yaml new file mode 100644 index 0000000000..86edd7ccd6 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/fluentbit_pod_label_test.yaml @@ -0,0 +1,48 @@ +suite: test fluent-bit pods labels +templates: + - templates/daemonset.yaml + - templates/configmap.yaml + - templates/persistentvolume.yaml +release: + name: my-release + namespace: my-namespace +tests: +- it: multiple pod labels are set properly + templates: + - templates/daemonset.yaml + set: + licenseKey: nr_license_key + podLabels: + key1: value1 + key2: value2 + asserts: + - equal: + path: spec.template.metadata.labels.key1 + value: value1 + template: templates/daemonset.yaml + - equal: + path: spec.template.metadata.labels.key2 + value: value2 + template: templates/daemonset.yaml +- it: single pod label set properly + templates: + - templates/daemonset.yaml + set: + licenseKey: nr_license_key + podLabels: + key1: value1 + asserts: + - equal: + path: spec.template.metadata.labels.key1 + value: value1 + template: templates/daemonset.yaml +- it: pod labels are not set + templates: + - templates/daemonset.yaml + set: + licenseKey: nr_license_key + asserts: + - notExists: + path: spec.template.metadata.labels.key1 + - notExists: + path: spec.template.metadata.labels.key2 \ No newline at end of file diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/fluentbit_sendmetrics_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/fluentbit_sendmetrics_test.yaml new file mode 100644 index 0000000000..f320172cbc --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/fluentbit_sendmetrics_test.yaml @@ -0,0 +1,74 @@ +suite: test fluentbit send metrics +templates: + - templates/configmap.yaml + - templates/daemonset.yaml + - templates/daemonset-windows.yaml +release: + name: sendmetrics-release + namespace: sendmetrics-namespace +tests: + + - it: sets requirement environment variables to send metrics + set: + licenseKey: nr_license_key + enableWindows: true + fluentBit.sendMetrics: true + asserts: + # Linux + - contains: + path: spec.template.spec.containers[0].env + content: + name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + template: templates/daemonset.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: HOSTNAME + valueFrom: + fieldRef: + fieldPath: metadata.name + template: templates/daemonset.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: SEND_OUTPUT_PLUGIN_METRICS + value: "true" + template: templates/daemonset.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: METRICS_HOST + value: "metric-api.newrelic.com" + template: templates/daemonset.yaml + # Windows + - contains: + path: spec.template.spec.containers[0].env + content: + name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + template: templates/daemonset-windows.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: HOSTNAME + valueFrom: + fieldRef: + fieldPath: metadata.name + template: templates/daemonset-windows.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: SEND_OUTPUT_PLUGIN_METRICS + value: "true" + template: templates/daemonset-windows.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: METRICS_HOST + value: "metric-api.newrelic.com" + template: templates/daemonset-windows.yaml diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/host_network_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/host_network_test.yaml new file mode 100644 index 0000000000..612d1d9a5f --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/host_network_test.yaml @@ -0,0 +1,46 @@ +suite: test hostNetwork options in fluent-bit pods +templates: + - templates/configmap.yaml + - templates/daemonset.yaml + - templates/daemonset-windows.yaml +release: + name: my-release + namespace: my-namespace +tests: + - it: daemonsets does not contain hostNetwork block when not provided + set: + licenseKey: nr_license_key + enableWindows: true + asserts: + - notExists: + path: spec.template.spec.hostNetwork + template: templates/daemonset.yaml + - notExists: + path: spec.template.spec.hostNetwork + template: templates/daemonset-windows.yaml + - it: daemonsets does not contain hostNetwork block when provided as false + set: + licenseKey: nr_license_key + enableWindows: true + hostNetwork: false + asserts: + - notExists: + path: spec.template.spec.hostNetwork + template: templates/daemonset.yaml + - notExists: + path: spec.template.spec.hostNetwork + template: templates/daemonset-windows.yaml + - it: daemonsets does contain hostNetwork=true when provided as true + set: + licenseKey: nr_license_key + enableWindows: true + hostNetwork: true + asserts: + - equal: + path: spec.template.spec.hostNetwork + value: true + template: templates/daemonset.yaml + - equal: + path: spec.template.spec.hostNetwork + value: true + template: templates/daemonset-windows.yaml \ No newline at end of file diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/images_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/images_test.yaml new file mode 100644 index 0000000000..3dc5b7a4e6 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/images_test.yaml @@ -0,0 +1,96 @@ +suite: test images settings +templates: + - templates/configmap.yaml + - templates/daemonset.yaml + - templates/daemonset-windows.yaml +release: + name: my-release + namespace: my-namespace +tests: + - it: image names are correct + templates: + - templates/daemonset.yaml + - templates/daemonset-windows.yaml + set: + licenseKey: nr_license_key + enableWindows: true + asserts: + - equal: + path: spec.template.spec.containers[0].image + value: newrelic/newrelic-fluentbit-output:2.0.0 + template: templates/daemonset.yaml + - equal: + path: spec.template.spec.containers[0].image + value: newrelic/newrelic-fluentbit-output:2.0.0-windows-ltsc-2019 + template: templates/daemonset-windows.yaml + documentIndex: 0 + - equal: + path: spec.template.spec.containers[0].image + value: newrelic/newrelic-fluentbit-output:2.0.0-windows-ltsc-2022 + template: templates/daemonset-windows.yaml + documentIndex: 1 + - it: global registry is used if set + templates: + - templates/daemonset.yaml + - templates/daemonset-windows.yaml + set: + licenseKey: nr_license_key + enableWindows: true + global: + images: + registry: global_registry + asserts: + - matchRegex: + path: spec.template.spec.containers[0].image + pattern: global_registry/.* + - it: local registry overrides global + templates: + - templates/daemonset.yaml + - templates/daemonset-windows.yaml + set: + licenseKey: nr_license_key + enableWindows: true + global: + images: + registry: global_registry + image: + registry: local_registry + asserts: + - matchRegex: + path: spec.template.spec.containers[0].image + pattern: local_registry/.* + - it: pullSecrets is used if defined + templates: + - templates/daemonset.yaml + - templates/daemonset-windows.yaml + set: + licenseKey: nr_license_key + enableWindows: true + image: + pullSecrets: + - name: regsecret + asserts: + - equal: + path: spec.template.spec.imagePullSecrets[0].name + value: regsecret + - it: pullSecrets are merged + templates: + - templates/daemonset.yaml + - templates/daemonset-windows.yaml + set: + licenseKey: nr_license_key + enableWindows: true + global: + images: + pullSecrets: + - name: global_regsecret + image: + pullSecrets: + - name: regsecret + asserts: + - equal: + path: spec.template.spec.imagePullSecrets[0].name + value: global_regsecret + - equal: + path: spec.template.spec.imagePullSecrets[1].name + value: regsecret diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/linux_volume_mount_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/linux_volume_mount_test.yaml new file mode 100644 index 0000000000..83d2a2c118 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/linux_volume_mount_test.yaml @@ -0,0 +1,37 @@ +suite: test fluent-bit linux mount for logs +templates: + - templates/configmap.yaml + - templates/daemonset.yaml +release: + name: my-release + namespace: my-namespace +tests: + - it: is set to /var by default an + set: + licenseKey: nr_license_key + asserts: + - equal: + path: spec.template.spec.containers[0].volumeMounts[1].mountPath + value: /var + template: templates/daemonset.yaml + - equal: + path: spec.template.spec.volumes[1].hostPath.path + value: /var + template: templates/daemonset.yaml + documentIndex: 0 + - it: is set to linuxMountPath if set + templates: + - templates/daemonset.yaml + set: + licenseKey: nr_license_key + fluentBit.linuxMountPath: /var/log + asserts: + - equal: + path: spec.template.spec.containers[0].volumeMounts[1].mountPath + value: /var/log + template: templates/daemonset.yaml + - equal: + path: spec.template.spec.volumes[1].hostPath.path + value: /var/log + template: templates/daemonset.yaml + documentIndex: 0 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/rbac_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/rbac_test.yaml new file mode 100644 index 0000000000..a8d85da98a --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/tests/rbac_test.yaml @@ -0,0 +1,48 @@ +suite: test RBAC creation +templates: + - templates/clusterrolebinding.yaml +release: + name: my-release + namespace: my-namespace +tests: + - it: template rbac if it is configured to do it + set: + rbac.create: true + asserts: + - hasDocuments: + count: 1 + + - it: don't template rbac if it is disabled + set: + rbac.create: false + asserts: + - hasDocuments: + count: 0 + + - it: RBAC points to the service account that is created by default + set: + rbac.create: true + serviceAccount.create: true + asserts: + - equal: + path: subjects[0].name + value: my-release-newrelic-logging + + - it: RBAC points to the service account the user supplies when serviceAccount is disabled + set: + rbac.create: true + serviceAccount.create: false + serviceAccount.name: sa-test + asserts: + - equal: + path: subjects[0].name + value: sa-test + + - it: RBAC points to the default service account when serviceAccount is disabled + set: + rbac.create: true + serviceAccount.create: false + asserts: + - equal: + path: subjects[0].name + value: default diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/values.yaml new file mode 100644 index 0000000000..c5d85b43f4 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-logging/values.yaml @@ -0,0 +1,361 @@ +# IMPORTANT: Specify your New Relic API key here. +# licenseKey: +# +# Optionally, specify a cluster name and log records can +# be filtered by cluster. +# cluster: +# +# or Specify secret which contains New Relic API key +# customSecretName: secret_name +# customSecretLicenseKey: secret_key +# +# The previous values can also be set as global so that they +# can be shared by other newrelic product's charts +# +# global: +# licenseKey: +# cluster: +# customSecretName: +# customSecretLicenseKey: +# +# IMPORTANT: if you use a kubernetes secret to specify the license, +# you have to manually provide the correct endpoint depending on +# whether your account is for the EU region or not. +# +# endpoint: https://log-api.newrelic.com/log/v1 + +fluentBit: + logLevel: "info" + path: "/var/log/containers/*.log" + linuxMountPath: /var + windowsPath: "C:\\var\\log\\containers\\*.log" + db: "/var/log/flb_kube.db" + windowsDb: "C:\\var\\log\\flb_kube.db" + criEnabled: true + k8sBufferSize: "32k" + k8sLoggingExclude: "false" + retryLimit: 5 + sendMetrics: false + extraEnv: [] + # extraEnv: + # - name: HTTPS_PROXY + # value: http://example.com:3128 + # - name: METADATA_NAME + # valueFrom: + # fieldRef: + # fieldPath: metadata.name + + # Indicates how fluent-bit database is persisted + persistence: + # Define the persistent mode for fluent-bit db, allowed options are `hostPath` (default), `none`, `persistentVolume`. + # - `hostPath` will use hostPath to store the db file on the node disk. + # - `none` will disable the fluent-bit db file, this could cause log duplication or data loss in case fluent-bit gets restarted. + # - `persistentVolume` will use a ReadWriteMany persistent volume to store the db file. This will override `fluentBit.db` path and use `/db/${NODE_NAME}-fb.db` file instead. + mode: "hostPath" + + # In case persistence.mode is set to persistentVolume this will be needed + persistentVolume: + # The storage class should allow ReadWriteMany mode + storageClass: + # Volume and claim size. + size: 10Gi + # If dynamicProvisioning is enabled the chart will create only the PersistentVolumeClaim + dynamicProvisioning: true + # If an existingVolume is provided, we'll use it instead creating a new one + existingVolume: + # If an existingVolumeClaim is provided, we'll use it instead creating a new one + existingVolumeClaim: + # In case you need to add annotations to the created volume or claim + annotations: + volume: {} + claim: {} + # In case you need to specify any other option to your volume or claim + extra: + volume: + # nfs: + # path: /tmp/ + # server: 1.1.1.1 + claim: {} + + + # New Relic default configuration for fluent-bit.conf (service, inputs, filters, outputs) + # and parsers.conf (parsers). The configuration below is not configured for lowDataMode and will + # send all attributes. If custom configuration is required, update these variables. + config: + # Note that Prometheus metric collection needs the HTTP server to be online at port 2020 (see fluentBit.config.metricInstrumentation) + service: | + [SERVICE] + Flush 1 + Log_Level ${LOG_LEVEL} + Daemon off + Parsers_File parsers.conf + HTTP_Server On + HTTP_Listen 0.0.0.0 + HTTP_Port 2020 + + inputs: | + [INPUT] + Name tail + Alias pod-logs-tailer + Tag kube.* + Path ${PATH} + multiline.parser ${LOG_PARSER} + DB ${FB_DB} + Mem_Buf_Limit 7MB + Skip_Long_Lines On + Refresh_Interval 10 + +# extraInputs: | +# [INPUT] +# Name dummy +# Tag dummy.log + + filters: | + [FILTER] + Name kubernetes + Alias kubernetes-enricher + Match kube.* + # We need the full DNS suffix as Windows only supports resolving names with this suffix + # See: https://kubernetes.io/docs/setup/production-environment/windows/intro-windows-in-kubernetes/#dns-limitations + Kube_URL https://kubernetes.default.svc.cluster.local:443 + Buffer_Size ${K8S_BUFFER_SIZE} + K8S-Logging.Exclude ${K8S_LOGGING_EXCLUDE} + + [FILTER] + Name record_modifier + Alias node-attributes-enricher + Match * + Record cluster_name "${CLUSTER_NAME}" + +# extraFilters: | +# [FILTER] +# Name grep +# Match * +# Exclude log lvl=debug* + + lowDataModeFilters: | + [FILTER] + Name kubernetes + Match kube.* + Alias kubernetes-enricher + # We need the full DNS suffix as Windows only supports resolving names with this suffix + # See: https://kubernetes.io/docs/setup/production-environment/windows/intro-windows-in-kubernetes/#dns-limitations + Kube_URL https://kubernetes.default.svc.cluster.local:443 + Buffer_Size ${K8S_BUFFER_SIZE} + K8S-Logging.Exclude ${K8S_LOGGING_EXCLUDE} + Labels Off + Annotations Off + + [FILTER] + Name nest + Match * + Alias kubernetes-attribute-lifter + Operation lift + Nested_under kubernetes + + [FILTER] + Name record_modifier + Match * + Alias node-attributes-enricher-filter + Record cluster_name "${CLUSTER_NAME}" + Allowlist_key container_name + Allowlist_key namespace_name + Allowlist_key pod_name + Allowlist_key stream + Allowlist_key message + Allowlist_key log + + outputs: | + [OUTPUT] + Name newrelic + Match * + Alias newrelic-logs-forwarder + licenseKey ${LICENSE_KEY} + endpoint ${ENDPOINT} + lowDataMode ${LOW_DATA_MODE} + sendMetrics ${SEND_OUTPUT_PLUGIN_METRICS} + Retry_Limit ${RETRY_LIMIT} + +# extraOutputs: | +# [OUTPUT] +# Name null +# Match * + +# parsers: | +# [PARSER] +# Name my_custom_parser +# Format json +# Time_Key time +# Time_Format %Y-%m-%dT%H:%M:%S.%L +# Time_Keep On + metricInstrumentation: | + [INPUT] + name prometheus_scrape + Alias fb-metrics-collector + host 127.0.0.1 + port 2020 + tag fb_metrics + metrics_path /api/v2/metrics/prometheus + scrape_interval 10s + + [OUTPUT] + Name prometheus_remote_write + Match fb_metrics + Alias fb-metrics-forwarder + Host ${METRICS_HOST} + Port 443 + Uri /prometheus/v1/write?prometheus_server=${CLUSTER_NAME} + Header Authorization Bearer ${LICENSE_KEY} + Tls On + # Windows pods using prometheus_remote_write currently have issues if TLS verify is On + Tls.verify Off + # User-defined labels + add_label app fluent-bit + add_label cluster_name "${CLUSTER_NAME}" + add_label pod_name ${HOSTNAME} + add_label node_name ${NODE_NAME} + add_label source kubernetes + +image: + repository: newrelic/newrelic-fluentbit-output +# registry: my_registry + tag: "" + pullPolicy: IfNotPresent + ## See https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod + pullSecrets: [] +# - name: regsecret + +# By default, the Linux DaemonSet will always be deployed, while the Windows DaemonSet(s) won't. +enableLinux: true +enableWindows: false +# For every entry in this Windows OS list, we will create an independent DaemonSet which will get deployed +# on Windows nodes running each specific Windows version and build number. Note that +# Windows containers can only be executed on hosts running the exact same Windows version and build number, +# because Kubernetes only supports process isolation and not Hyper-V isolation (as of September 2021) +windowsOsList: + # We aim to support (limited to LTSC2019/LTSC2022 using GitHub actions, see https://github.com/actions/runner-images/tree/main/images/win): + # https://kubernetes.io/docs/setup/production-environment/windows/intro-windows-in-kubernetes/#windows-os-version-support + - version: ltsc2019 + imageTagSuffix: windows-ltsc-2019 + buildNumber: 10.0.17763 + - version: ltsc2022 + imageTagSuffix: windows-ltsc-2022 + buildNumber: 10.0.20348 + +# Default set of resources assigned to the DaemonSet pods +resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 250m + memory: 64Mi + +rbac: + # Specifies whether RBAC resources should be created + create: true + pspEnabled: false + +serviceAccount: + # Specifies whether a ServiceAccount should be created + create: + # The name of the ServiceAccount to use. + # If not set and create is true, a name is generated using the fullname template + name: + # Specify any annotations to add to the ServiceAccount + annotations: {} + +# Optionally configure ports to expose metrics on /api/v1/metrics/prometheus +# See - https://docs.fluentbit.io/manual/administration/monitoring +exposedPorts: [] +# - containerPort: 2020 +# hostPort: 2020 +# name: metrics +# protocol: TCP + +# If you wish to provide additional labels to apply to the pod(s), specify +# them here +# podLabels: + +# Pod scheduling priority +# Ref: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/ +# priorityClassName: high-priority + +# Node affinity rules +# Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity +# +# IMPORTANT # +# ######### # +# When .Values.global.fargate == true, the chart will automatically add the required affinity rules to exclude +# the DaemonSet from Fargate nodes. There is no need to manually touch this property achieve this. +# This automatic exclusion will, however, not take place if this value is overridden: Setting this to a +# non-empty value WHEN deploying in EKS Fargate (global.fargate == true) requires the user to manually +# include in their custom ruleset an exclusion for nodes with "eks.amazonaws.com/compute-type: fargate", as +# the New Relic DaemonSet MUST NOT be deployed on fargate nodes, as the operator takes care of injecting it +# as a sidecar instead. +# Please refer to the daemonset.yaml template for more details on how to achieve this. +nodeAffinity: {} + +# Node labels for pod assignment +# Ref: https://kubernetes.io/docs/user-guide/node-selection/ +# Note that the Linux DaemonSet already contains a node selector label based on their OS (kubernetes.io/os: linux). +nodeSelector: {} + +# Note that the Windows DaemonSet already contains a node selector label based on their OS (kubernetes.io/os: windows). +# and build number (node.kubernetes.io/windows-build: {{ .buildNumber }}, to ensure that each version of the DaemonSet +# gets deployed only on those Windows nodes running the exact same Windows version and build number. Note that +# Windows containers can only be executed on hosts running the exact same Windows version and build number. +windowsNodeSelector: {} + +# These are default tolerations to be able to run the New Relic Kubernetes integration. +tolerations: + - operator: "Exists" + effect: "NoSchedule" + - operator: "Exists" + effect: "NoExecute" + +updateStrategy: RollingUpdate + +# Sends data to staging, can be set as a global. +# global.nrStaging +nrStaging: false + +daemonSet: + # Annotations to add to the DaemonSet. + annotations: {} + +# Annotations to add to the resulting Pods of the DaemonSet. +podAnnotations: {} + +# If host network should be enabled for fluentbit pods. +# There are some inputs like UDP which will require this setting to be true as they need to bind to the host network. +hostNetwork: + +# When low data mode is enabled only minimal attributes are added to the logs. Kubernetes labels and +# annotations are not included. The plugin.type, plugin.version and plugin.source attributes are minified +# into the plugin.source attribute. +# Can be set as a global: global.lowDataMode +# lowDataMode: false + +extraVolumes: [] +# - name: systemdlog +# hostPath: +# path: /run/log/journal + +extraVolumeMounts: [] +# - name: systemdlog +# mountPath: /run/log/journal + +initContainers: +# - name: init +# image: busybox +# command: ["sh", "-c", 'echo "hello world"'] + +windows: + initContainers: +# - name: init +# image: ... +# command: [...] + +# -- Sets pod dnsConfig. Can also be configured with `global.dnsConfig` +dnsConfig: {} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/Chart.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/Chart.yaml new file mode 100644 index 0000000000..acd3077d4a --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/Chart.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +appVersion: 2.1.4 +description: A Helm chart for the New Relic Pixie integration. +home: https://hub.docker.com/u/newrelic +icon: https://newrelic.com/assets/newrelic/source/NewRelic-logo-square.svg +keywords: +- newrelic +- pixie +- monitoring +maintainers: +- name: nserrino +- name: philkuz +- name: htroisi +- name: vuqtran88 +name: newrelic-pixie +sources: +- https://github.com/newrelic/ +version: 2.1.4 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/README.md b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/README.md new file mode 100644 index 0000000000..228a3676d3 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/README.md @@ -0,0 +1,166 @@ +# newrelic-pixie + +## Chart Details + +This chart will deploy the New Relic Pixie Integration. + +IMPORTANT: In order to retrieve the Pixie cluster id from the `pl-cluster-secrets` the integration needs to be deployed in the same namespace as Pixie. By default, Pixie is installed in the `pl` namespace. Alternatively the `clusterId` can be configured manually when installing the chart. In this case the integration can be deployed to any namespace. + +## Configuration + +| Parameter | Description | Default | +| ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | +| `global.cluster` - `cluster` | The cluster name for the Kubernetes cluster. Required. | | +| `global.licenseKey` - `licenseKey` | The New Relic license key (stored in a secret). Required. | | +| `global.lowDataMode` - `lowDataMode` | If `true`, the integration performs heavier sampling on the Pixie span data and sets the collect interval to 15 seconds instead of 10 seconds. | false | +| `global.nrStaging` - `nrStaging` | Send data to staging (requires a staging license key). | false | +| `apiKey` | The Pixie API key (stored in a secret). Required. | | +| `clusterId` | The Pixie cluster id. Optional. Read from the `pl-cluster-secrets` secret if empty. | | +| `endpoint` | The Pixie endpoint. Required when using Pixie Open Source. | | +| `verbose` | Whether the integration should run in verbose mode or not. | false | +| `global.customSecretName` - `customSecretName` | Name of an existing Secret object, not created by this chart, where the New Relic license is stored | | +| `global.customSecretLicenseKey` - `customSecretLicenseKey` | Key in the existing Secret object, indicated by `customSecretName`, where the New Relic license key is stored. | | +| `image.pullSecrets` | Image pull secrets. | `nil` | +| `customSecretApiKeyName` | Name of an existing Secret object, not created by this chart, where the Pixie API key is stored. | | +| `customSecretApiKeyKey` | Key in the existing Secret object, indicated by `customSecretApiKeyName`, where the Pixie API key is stored. | | +| `podLabels` | Labels added to each Job pod | `{}` | +| `podAnnotations` | Annotations added to each Job pod | `{}` | +| `job.annotations` | Annotations added to the `newrelic-pixie` Job resource | `{}` | +| `job.labels` | Annotations added to the `newrelic-pixie` Job resource | `{}` | +| `nodeSelector` | Node label to use for scheduling. | `{}` | +| `tolerations` | List of node taints to tolerate (requires Kubernetes >= 1.6). | `[]` | +| `affinity` | Node affinity to use for scheduling. | `{}` | +| `proxy` | Set proxy to connect to Pixie Cloud and New Relic. | | +| `customScripts` | YAML containing custom scripts for long-term data retention. The results of the custom scripts will be stored in New Relic. See [custom scripts](#custom-scripts) for YAML format. | `{}` | +| `customScriptsConfigMap` | Name of an existing ConfigMap object containing custom script for long-term data retention. This configuration takes precedence over `customScripts`. | | +| `excludeNamespacesRegex` | Observability data for namespaces matching this RE2 regex is not sent to New Relic. If empty, observability data for all namespaces is sent to New Relic. | | +| `excludePodsRegex` | Observability data for pods (across all namespaces) matching this RE2 regex is not sent to New Relic. If empty, observability data for all pods (in non-excluded namespaces) is sent to New Relic. | | + +## Example + +Make sure you have [added the New Relic chart repository.](../../README.md#installing-charts) + +Then, to install this chart, run the following command: + +```sh +helm install newrelic/newrelic-pixie \ + --set cluster= \ + --set licenseKey= \ + --set apiKey= \ + --namespace pl \ + --generate-name +``` + +## Globals + +**Important:** global parameters have higher precedence than locals with the same name. + +These are meant to be used when you are writing a chart with subcharts. It helps to avoid +setting values multiple times on different subcharts. + +More information on globals and subcharts can be found at [Helm's official documentation](https://helm.sh/docs/topics/chart_template_guide/subcharts_and_globals/). + +| Parameter | +| ------------------------------- | +| `global.cluster` | +| `global.licenseKey` | +| `global.customSecretName` | +| `global.customSecretLicenseKey` | +| `global.lowDataMode` | +| `global.nrStaging` | + +## Custom scripts + +Custom scripts can either be configured directly in `customScripts` or be provided through an existing ConfigMap `customScriptsConfigMap`. + +The entries in the ConfigMap should contain file-like keys with the `.yaml` extension. Each file in the ConfigMap should be valid YAML and contain the following keys: + + * name (string): the name of the script + * description (string): description of the script + * frequencyS (int): frequency to execute the script in seconds + * scripts (string): the actual PXL script to execute + * addExcludes (optional boolean, `false` by default): add pod and namespace excludes to the custom script + +For more detailed information about the custom scripts see [the New Relic Pixie integration repo](https://github.com/newrelic/newrelic-pixie-integration/). + +```yaml +customScripts: + custom1.yaml: | + name: "custom1" + description: "Custom script 1" + frequencyS: 60 + script: | + import px + + df = px.DataFrame(table='http_events', start_time=px.plugin.start_time) + + ns_prefix = df.ctx['namespace'] + '/' + df.container = df.ctx['container_name'] + df.pod = px.strip_prefix(ns_prefix, df.ctx['pod']) + df.service = px.strip_prefix(ns_prefix, df.ctx['service']) + df.namespace = df.ctx['namespace'] + + df.status_code = df.resp_status + + df = df.groupby(['status_code', 'pod', 'container','service', 'namespace']).agg( + latency_min=('latency', px.min), + latency_max=('latency', px.max), + latency_sum=('latency', px.sum), + latency_count=('latency', px.count), + time_=('time_', px.max), + ) + + df.latency_min = df.latency_min / 1000000 + df.latency_max = df.latency_max / 1000000 + df.latency_sum = df.latency_sum / 1000000 + + df.cluster_name = px.vizier_name() + df.cluster_id = px.vizier_id() + df.pixie = 'pixie' + + px.export( + df, px.otel.Data( + resource={ + 'service.name': df.service, + 'k8s.container.name': df.container, + 'service.instance.id': df.pod, + 'k8s.pod.name': df.pod, + 'k8s.namespace.name': df.namespace, + 'px.cluster.id': df.cluster_id, + 'k8s.cluster.name': df.cluster_name, + 'instrumentation.provider': df.pixie, + }, + data=[ + px.otel.metric.Summary( + name='http.server.duration', + description='measures the duration of the inbound HTTP request', + # Unit is not supported yet + # unit='ms', + count=df.latency_count, + sum=df.latency_sum, + quantile_values={ + 0.0: df.latency_min, + 1.0: df.latency_max, + }, + attributes={ + 'http.status_code': df.status_code, + }, + )], + ), + ) +``` + + +## Resources + +The default set of resources assigned to the pods is shown below: + +```yaml +resources: + limits: + memory: 250M + requests: + cpu: 100m + memory: 250M +``` + diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/ci/test-values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/ci/test-values.yaml new file mode 100644 index 0000000000..580f9b0ba1 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/ci/test-values.yaml @@ -0,0 +1,5 @@ +global: + licenseKey: 1234567890abcdef1234567890abcdef12345678 + apiKey: 1234567890abcdef + cluster: test-cluster +clusterId: foobar diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/templates/NOTES.txt b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/templates/NOTES.txt new file mode 100644 index 0000000000..d542838894 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/templates/NOTES.txt @@ -0,0 +1,27 @@ +{{- if (include "newrelic-pixie.areValuesValid" .) }} + +Your deployment of the New Relic Pixie integration is complete. + +Please ensure this integration is deployed in the same namespace +as Pixie or manually specify the clusterId. +{{- else -}} +############################################################### +#### ERROR: You did not set all the required values. #### +############################################################### + +This deployment will be incomplete until you set all the required values: + +* Cluster name +* New Relic license key +* Pixie API key + +For a simple installation to be fixed, run: + + helm upgrade {{ .Release.Name }} \ + --set cluster=YOUR-CLUSTER-NAME \ + --set licenseKey=YOUR-LICENSE-KEY \ + --set apiKey=YOUR-API-KEY \ + -n {{ .Release.Namespace }} \ + newrelic/newrelic-pixie + +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/templates/_helpers.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/templates/_helpers.tpl new file mode 100644 index 0000000000..40b9c68df1 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/templates/_helpers.tpl @@ -0,0 +1,172 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "newrelic-pixie.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "newrelic-pixie.namespace" -}} +{{- if .Values.namespace -}} + {{- .Values.namespace -}} +{{- else -}} + {{- .Release.Namespace | default "pl" -}} +{{- 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). +*/}} +{{- define "newrelic-pixie.fullname" -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if ne $name .Release.Name -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s" $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} + +{{/* Generate basic labels */}} +{{- define "newrelic-pixie.labels" }} +app: {{ template "newrelic-pixie.name" . }} +app.kubernetes.io/name: {{ include "newrelic-pixie.name" . }} +chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} +heritage: {{.Release.Service }} +release: {{.Release.Name }} +{{- end }} + +{{- define "newrelic-pixie.cluster" -}} +{{- if .Values.global -}} + {{- if .Values.global.cluster -}} + {{- .Values.global.cluster -}} + {{- else -}} + {{- .Values.cluster | default "" -}} + {{- end -}} +{{- else -}} + {{- .Values.cluster | default "" -}} +{{- end -}} +{{- end -}} + +{{- define "newrelic-pixie.nrStaging" -}} +{{- if .Values.global }} + {{- if .Values.global.nrStaging }} + {{- .Values.global.nrStaging -}} + {{- end -}} +{{- else if .Values.nrStaging }} + {{- .Values.nrStaging -}} +{{- end -}} +{{- end -}} + +{{- define "newrelic-pixie.licenseKey" -}} +{{- if .Values.global}} + {{- if .Values.global.licenseKey }} + {{- .Values.global.licenseKey -}} + {{- else -}} + {{- .Values.licenseKey | default "" -}} + {{- end -}} +{{- else -}} + {{- .Values.licenseKey | default "" -}} +{{- end -}} +{{- end -}} + +{{- define "newrelic-pixie.apiKey" -}} +{{- if .Values.global}} + {{- if .Values.global.apiKey }} + {{- .Values.global.apiKey -}} + {{- else -}} + {{- .Values.apiKey | default "" -}} + {{- end -}} +{{- else -}} + {{- .Values.apiKey | default "" -}} +{{- end -}} +{{- end -}} + +{{- /* +adapted from https://github.com/newrelic/helm-charts/blob/af747af93fb5b912374196adc59b552965b6e133/library/common-library/templates/_low-data-mode.tpl +TODO: actually use common-library chart dep +*/ -}} +{{- /* +Abstraction of the lowDataMode toggle. +This helper allows to override the global `.global.lowDataMode` with the value of `.lowDataMode`. +Returns "true" if `lowDataMode` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic-pixie.lowDataMode" -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if (get .Values "lowDataMode" | kindIs "bool") -}} + {{- if .Values.lowDataMode -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.lowDataMode" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.lowDataMode -}} + {{- end -}} +{{- else -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "lowDataMode" | kindIs "bool" -}} + {{- if $global.lowDataMode -}} + {{- $global.lowDataMode -}} + {{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the customSecretName where the New Relic license is being stored. +*/}} +{{- define "newrelic-pixie.customSecretName" -}} +{{- if .Values.global }} + {{- if .Values.global.customSecretName }} + {{- .Values.global.customSecretName -}} + {{- else -}} + {{- .Values.customSecretName | default "" -}} + {{- end -}} +{{- else -}} + {{- .Values.customSecretName | default "" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the customSecretApiKeyName where the Pixie API key is being stored. +*/}} +{{- define "newrelic-pixie.customSecretApiKeyName" -}} + {{- .Values.customSecretApiKeyName | default "" -}} +{{- end -}} + +{{/* +Return the customSecretLicenseKey +*/}} +{{- define "newrelic-pixie.customSecretLicenseKey" -}} +{{- if .Values.global }} + {{- if .Values.global.customSecretLicenseKey }} + {{- .Values.global.customSecretLicenseKey -}} + {{- else -}} + {{- .Values.customSecretLicenseKey | default "" -}} + {{- end -}} +{{- else -}} + {{- .Values.customSecretLicenseKey | default "" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the customSecretApiKeyKey +*/}} +{{- define "newrelic-pixie.customSecretApiKeyKey" -}} + {{- .Values.customSecretApiKeyKey | default "" -}} +{{- end -}} + +{{/* +Returns if the template should render, it checks if the required values +licenseKey and cluster are set. +*/}} +{{- define "newrelic-pixie.areValuesValid" -}} +{{- $cluster := include "newrelic-pixie.cluster" . -}} +{{- $licenseKey := include "newrelic-pixie.licenseKey" . -}} +{{- $apiKey := include "newrelic-pixie.apiKey" . -}} +{{- $customSecretName := include "newrelic-pixie.customSecretName" . -}} +{{- $customSecretLicenseKey := include "newrelic-pixie.customSecretLicenseKey" . -}} +{{- $customSecretApiKeyKey := include "newrelic-pixie.customSecretApiKeyKey" . -}} +{{- and (or (and $licenseKey $apiKey) (and $customSecretName $customSecretLicenseKey $customSecretApiKeyKey)) $cluster}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/templates/configmap.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/templates/configmap.yaml new file mode 100644 index 0000000000..19f7fe61ac --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/templates/configmap.yaml @@ -0,0 +1,12 @@ +{{- if (include "newrelic-pixie.areValuesValid" .) }} +{{- if .Values.customScripts }} +apiVersion: v1 +kind: ConfigMap +metadata: + namespace: {{ template "newrelic-pixie.namespace" . }} + labels: {{ include "newrelic-pixie.labels" . | indent 4 }} + name: {{ template "newrelic-pixie.fullname" . }}-scripts +data: +{{- toYaml .Values.customScripts | nindent 2 }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/templates/job.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/templates/job.yaml new file mode 100644 index 0000000000..89b97514fa --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/templates/job.yaml @@ -0,0 +1,164 @@ +{{- if (include "newrelic-pixie.areValuesValid" .) }} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "newrelic-pixie.fullname" . }} + namespace: {{ template "newrelic-pixie.namespace" . }} + labels: + {{- include "newrelic-pixie.labels" . | trim | nindent 4}} + {{- if ((.Values.job).labels) }} + {{- toYaml .Values.job.labels | nindent 4 }} + {{- end }} + {{- if ((.Values.job).annotations) }} + annotations: + {{ toYaml .Values.job.annotations | nindent 4 | trim }} + {{- end }} +spec: + backoffLimit: 4 + ttlSecondsAfterFinished: 600 + template: + metadata: + labels: + app.kubernetes.io/name: {{ template "newrelic-pixie.name" . }} + release: {{.Release.Name }} + {{- if .Values.podLabels }} + {{- toYaml .Values.podLabels | nindent 8 }} + {{- end }} + {{- if .Values.podAnnotations }} + annotations: + {{- toYaml .Values.podAnnotations | nindent 8 }} + {{- end }} + spec: + {{- if .Values.image.pullSecrets }} + imagePullSecrets: + {{- toYaml .Values.image.pullSecrets | nindent 8 }} + {{- end }} + restartPolicy: Never + initContainers: + - name: cluster-registration-wait + image: gcr.io/pixie-oss/pixie-dev-public/curl:1.0 + command: ['sh', '-c', 'set -x; + URL="https://${SERVICE_NAME}:${SERVICE_PORT}/readyz"; + until [ $(curl -m 0.5 -s -o /dev/null -w "%{http_code}" -k ${URL}) -eq 200 ]; do + echo "Waiting for cluster registration. If this takes too long check the vizier-cloud-connector logs." + sleep 2; + done; + '] + env: + # The name of the Pixie service which connects to Pixie Cloud for cluster registration. + - name: SERVICE_NAME + value: "vizier-cloud-connector-svc" + - name: SERVICE_PORT + value: "50800" + containers: + - name: {{ template "newrelic-pixie.name" . }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: "{{ .Values.image.pullPolicy }}" + env: + - name: CLUSTER_NAME + value: {{ template "newrelic-pixie.cluster" . }} + - name: NR_LICENSE_KEY + valueFrom: + secretKeyRef: + {{- if (include "newrelic-pixie.licenseKey" .) }} + name: {{ template "newrelic-pixie.fullname" . }}-secrets + key: newrelicLicenseKey + {{- else }} + name: {{ include "newrelic-pixie.customSecretName" . }} + key: {{ include "newrelic-pixie.customSecretLicenseKey" . }} + {{- end }} + - name: PIXIE_API_KEY + valueFrom: + secretKeyRef: + {{- if (include "newrelic-pixie.apiKey" .) }} + name: {{ template "newrelic-pixie.fullname" . }}-secrets + key: pixieApiKey + {{- else }} + name: {{ include "newrelic-pixie.customSecretApiKeyName" . }} + key: {{ include "newrelic-pixie.customSecretApiKeyKey" . }} + {{- end }} + - name: PIXIE_CLUSTER_ID + {{- if .Values.clusterId }} + value: {{ .Values.clusterId -}} + {{- else }} + valueFrom: + secretKeyRef: + key: cluster-id + name: pl-cluster-secrets + {{- end }} + {{- if .Values.verbose }} + - name: VERBOSE + value: "true" + {{- end }} + {{- if (include "newrelic-pixie.lowDataMode" .) }} + - name: COLLECT_INTERVAL_SEC + value: "15" + - name: HTTP_SPAN_LIMIT + value: "750" + - name: DB_SPAN_LIMIT + value: "250" + {{- else }} + - name: COLLECT_INTERVAL_SEC + value: "10" + - name: HTTP_SPAN_LIMIT + value: "1500" + - name: DB_SPAN_LIMIT + value: "500" + {{- end }} + {{- if (include "newrelic-pixie.nrStaging" .) }} + - name: NR_OTLP_HOST + value: "staging-otlp.nr-data.net:4317" + {{- end }} + {{- if or .Values.endpoint (include "newrelic-pixie.nrStaging" .) }} + - name: PIXIE_ENDPOINT + {{- if .Values.endpoint }} + value: {{ .Values.endpoint | quote }} + {{- else }} + value: "staging.withpixie.dev:443" + {{- end }} + {{- end }} + {{- if .Values.proxy }} + - name: HTTP_PROXY + value: {{ .Values.proxy | quote }} + - name: HTTPS_PROXY + value: {{ .Values.proxy | quote }} + {{- end }} + {{- if .Values.excludePodsRegex }} + - name: EXCLUDE_PODS_REGEX + value: {{ .Values.excludePodsRegex | quote }} + {{- end }} + {{- if .Values.excludeNamespacesRegex }} + - name: EXCLUDE_NAMESPACES_REGEX + value: {{ .Values.excludeNamespacesRegex | quote }} + {{- end }} + {{- if .Values.resources }} + resources: + {{- toYaml .Values.resources | nindent 10 }} + {{- end }} + {{- if or .Values.customScriptsConfigMap .Values.customScripts }} + volumeMounts: + - name: scripts + mountPath: "/scripts" + readOnly: true + volumes: + - name: scripts + configMap: + {{- if .Values.customScriptsConfigMap }} + name: {{ .Values.customScriptsConfigMap }} + {{- else }} + name: {{ template "newrelic-pixie.fullname" . }}-scripts + {{- end}} + {{- end }} + {{- if $.Values.nodeSelector }} + nodeSelector: + {{- toYaml $.Values.nodeSelector | nindent 8 }} + {{- end }} + {{- if .Values.tolerations }} + tolerations: + {{- toYaml .Values.tolerations | nindent 8 }} + {{- end }} + {{- if .Values.affinity }} + affinity: + {{- toYaml .Values.affinity | nindent 8 }} + {{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/templates/secret.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/templates/secret.yaml new file mode 100644 index 0000000000..4d95618772 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/templates/secret.yaml @@ -0,0 +1,20 @@ +{{- if (include "newrelic-pixie.areValuesValid" .) }} +{{- $licenseKey := include "newrelic-pixie.licenseKey" . -}} +{{- $apiKey := include "newrelic-pixie.apiKey" . -}} +{{- if or $apiKey $licenseKey}} +apiVersion: v1 +kind: Secret +metadata: + namespace: {{ template "newrelic-pixie.namespace" . }} + labels: {{ include "newrelic-pixie.labels" . | indent 4 }} + name: {{ template "newrelic-pixie.fullname" . }}-secrets +type: Opaque +data: + {{- if $licenseKey }} + newrelicLicenseKey: {{ $licenseKey | b64enc }} + {{- end }} + {{- if $apiKey }} + pixieApiKey: {{ include "newrelic-pixie.apiKey" . | b64enc -}} + {{- end }} +{{- end }} +{{- end}} \ No newline at end of file diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/tests/configmap.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/tests/configmap.yaml new file mode 100644 index 0000000000..ecba6363bd --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/tests/configmap.yaml @@ -0,0 +1,44 @@ +suite: test custom scripts ConfigMap +templates: + - templates/configmap.yaml +tests: + - it: ConfigMap is created + set: + cluster: "test-cluster" + licenseKey: "license123" + apiKey: "api123" + customScripts: + custom1.yaml: | + name: "custom1" + description: "Custom script 1" + frequencyS: 60 + script: | + import px + df = px.DataFrame(table='http_events', start_time=px.plugin.start_time) + asserts: + - isKind: + of: ConfigMap + - equal: + path: data.custom1\.yaml + value: |- + name: "custom1" + description: "Custom script 1" + frequencyS: 60 + script: | + import px + df = px.DataFrame(table='http_events', start_time=px.plugin.start_time) + - equal: + path: metadata.name + value: RELEASE-NAME-newrelic-pixie-scripts + - equal: + path: metadata.namespace + value: NAMESPACE + - it: ConfigMap is empty + set: + cluster: "test-cluster" + licenseKey: "license123" + apiKey: "api123" + customScripts: {} + asserts: + - hasDocuments: + count: 0 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/tests/jobs.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/tests/jobs.yaml new file mode 100644 index 0000000000..03a3d86b8f --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/tests/jobs.yaml @@ -0,0 +1,138 @@ +suite: test job +templates: + - templates/job.yaml +tests: + - it: Test primary fields of job + set: + cluster: "test-cluster" + licenseKey: "license123" + apiKey: "api123" + image: + tag: "latest" + asserts: + - isKind: + of: Job + - equal: + path: "metadata.name" + value: "RELEASE-NAME-newrelic-pixie" + - equal: + path: "metadata.namespace" + value: "NAMESPACE" + - equal: + path: "spec.template.spec.containers[0].image" + value: "newrelic/newrelic-pixie-integration:latest" + - equal: + path: "spec.template.spec.containers[0].env" + value: + - name: CLUSTER_NAME + value: test-cluster + - name: NR_LICENSE_KEY + valueFrom: + secretKeyRef: + key: newrelicLicenseKey + name: RELEASE-NAME-newrelic-pixie-secrets + - name: PIXIE_API_KEY + valueFrom: + secretKeyRef: + key: pixieApiKey + name: RELEASE-NAME-newrelic-pixie-secrets + - name: PIXIE_CLUSTER_ID + valueFrom: + secretKeyRef: + key: cluster-id + name: pl-cluster-secrets + - isEmpty: + path: "spec.template.spec.containers[0].volumeMounts" + - isEmpty: + path: "spec.template.spec.volumes" + - it: Job with clusterId + set: + cluster: "test-cluster" + licenseKey: "license123" + apiKey: "api123" + clusterId: "cid123" + asserts: + - equal: + path: "spec.template.spec.containers[0].env" + value: + - name: CLUSTER_NAME + value: test-cluster + - name: NR_LICENSE_KEY + valueFrom: + secretKeyRef: + key: newrelicLicenseKey + name: RELEASE-NAME-newrelic-pixie-secrets + - name: PIXIE_API_KEY + valueFrom: + secretKeyRef: + key: pixieApiKey + name: RELEASE-NAME-newrelic-pixie-secrets + - name: PIXIE_CLUSTER_ID + value: "cid123" + - it: Job with Pixie endpoint + set: + cluster: "test-cluster" + licenseKey: "license123" + apiKey: "api123" + clusterId: "cid123" + endpoint: "withpixie.ai:443" + asserts: + - equal: + path: "spec.template.spec.containers[0].env" + value: + - name: CLUSTER_NAME + value: test-cluster + - name: NR_LICENSE_KEY + valueFrom: + secretKeyRef: + key: newrelicLicenseKey + name: RELEASE-NAME-newrelic-pixie-secrets + - name: PIXIE_API_KEY + valueFrom: + secretKeyRef: + key: pixieApiKey + name: RELEASE-NAME-newrelic-pixie-secrets + - name: PIXIE_CLUSTER_ID + value: "cid123" + - name: PIXIE_ENDPOINT + value: "withpixie.ai:443" + - it: Job with custom scripts + set: + cluster: "test-cluster" + licenseKey: "license123" + apiKey: "api123" + customScripts: + custom1.yaml: | + name: "custom1" + asserts: + - equal: + path: "spec.template.spec.containers[0].volumeMounts" + value: + - name: scripts + mountPath: "/scripts" + readOnly: true + - equal: + path: "spec.template.spec.volumes[0]" + value: + name: scripts + configMap: + name: RELEASE-NAME-newrelic-pixie-scripts + - it: Job with custom script in defined ConfigMap + set: + cluster: "test-cluster" + licenseKey: "license123" + apiKey: "api123" + customScriptsConfigMap: "myconfigmap" + asserts: + - equal: + path: "spec.template.spec.containers[0].volumeMounts" + value: + - name: scripts + mountPath: "/scripts" + readOnly: true + - equal: + path: "spec.template.spec.volumes[0]" + value: + name: scripts + configMap: + name: myconfigmap diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/values.yaml new file mode 100644 index 0000000000..4103d54e91 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-pixie/values.yaml @@ -0,0 +1,70 @@ +# IMPORTANT: The Kubernetes cluster name +# https://docs.newrelic.com/docs/kubernetes-monitoring-integration +# cluster: "" + +# The New Relic license key +# licenseKey: "" + +# The Pixie API key +# apiKey: "" + +# The Pixie Cluster Id +# clusterId: + +# The Pixie endpoint +# endpoint: + +# If you already have a secret where the New Relic license key is stored, indicate its name here +# customSecretName: +# The key in the customSecretName secret that contains the New Relic license key +# customSecretLicenseKey: +# If you already have a secret where the Pixie API key is stored, indicate its name here +# customSecretApiKeyName: +# The key in the customSecretApiKeyName secret that contains the Pixie API key +# customSecretApiKeyKey: + +image: + repository: newrelic/newrelic-pixie-integration + tag: "" + pullPolicy: IfNotPresent + pullSecrets: [] + # - name: regsecret + +resources: + limits: + memory: 250M + requests: + cpu: 100m + memory: 250M + +# -- Annotations to add to the pod. +podAnnotations: {} +# -- Additional labels for chart pods +podLabels: {} + +job: + # job.annotations -- Annotations to add to the Job. + annotations: {} + # job.labels -- Labels to add to the Job. + labels: {} + +proxy: {} + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +customScripts: {} +# Optionally the scripts can be provided in an already existing ConfigMap: +# customScriptsConfigMap: + +excludeNamespacesRegex: +excludePodsRegex: + +# When low data mode is enabled the integration performs heavier sampling on the Pixie span data +# and sets the collect interval to 15 seconds instead of 10 seconds. +# Can be set as a global: global.lowDataMode or locally as newrelic-pixie.lowDataMode +# @default -- false +lowDataMode: diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/.helmignore b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/.helmignore new file mode 100644 index 0000000000..0e8a0eb36f --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/.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 +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/Chart.lock b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/Chart.lock new file mode 100644 index 0000000000..18bbb9ef4b --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: common-library + repository: https://helm-charts.newrelic.com + version: 1.2.0 +digest: sha256:fa87cb007564a39a72739a3e850a91d6b03c0fc27a1115deac042b3ef77b4142 +generated: "2024-06-21T18:14:01.260095101Z" diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/Chart.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/Chart.yaml new file mode 100644 index 0000000000..08d0cf6dc5 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/Chart.yaml @@ -0,0 +1,22 @@ +annotations: + configuratorVersion: 1.17.3 +apiVersion: v2 +appVersion: v2.37.8 +dependencies: +- name: common-library + repository: https://helm-charts.newrelic.com + version: 1.2.0 +description: A Helm chart to deploy Prometheus with New Relic Prometheus Configurator. +keywords: +- newrelic +- prometheus +maintainers: +- name: juanjjaramillo + url: https://github.com/juanjjaramillo +- name: csongnr + url: https://github.com/csongnr +- name: dbudziwojskiNR + url: https://github.com/dbudziwojskiNR +name: newrelic-prometheus-agent +type: application +version: 1.14.3 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/README.md b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/README.md new file mode 100644 index 0000000000..069b9a79b6 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/README.md @@ -0,0 +1,244 @@ +# newrelic-prometheus-agent + +A Helm chart to deploy Prometheus with New Relic Prometheus Configurator. + +# Description + +This chart deploys Prometheus Server in Agent mode configured by the `newrelic-prometheus-configurator`. + +The solution is deployed as a StatefulSet for sharding proposes. +Each Pod will execute the `newrelic-prometheus-configurator` init container which will convert the provided config to a config file in the Prometheus format. Once the init container finishes and saves the config in a shared volume, the container running Prometheus in Agent mode will start. + +```mermaid +graph LR + subgraph pod[Pod] + direction TB + subgraph volume[shared volume] + plain[Prometheus Config] + end + + subgraph init-container[init Container] + configurator[Configurator] --> plain[Prometheus Config] + end + + subgraph container[Main Container] + plain[Prometheus Config] --> prom-agent[Prometheus-Agent] + end + + end + + subgraph configMap + NewRelic-Config --> configurator[Configurator] + end + +classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000; +classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; +classDef pod fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5; +class configurator,init-container,container,prom-agent k8s; +class volume plain; +class pod pod; + +``` + +# Helm installation + +You can install this chart using [`nri-bundle`](https://github.com/newrelic/helm-charts/tree/master/charts/nri-bundle) located in the +[helm-charts repository](https://github.com/newrelic/helm-charts) or directly from this repository by adding this Helm repository: + +```shell +helm repo add newrelic-prometheus https://newrelic.github.io/newrelic-prometheus-configurator +helm upgrade --install newrelic newrelic-prometheus/newrelic-prometheus-agent -f your-custom-values.yaml +``` + +## Values managed globally + +This chart implements the [New Relic's common Helm library](https://github.com/newrelic/helm-charts/tree/master/library/common-library) which +means that it honors a wide range of defaults and globals common to most New Relic Helm charts. + +Options that can be defined globally include `affinity`, `nodeSelector`, `tolerations`, `proxy` and others. The full list can be found at +[user's guide of the common library](https://github.com/newrelic/helm-charts/blob/master/library/common-library/README.md). + +## Chart particularities + +### Configuration + +The configuration used is similar to the [Prometheus configuration](https://prometheus.io/docs/prometheus/latest/configuration/configuration/), but it includes some syntactic sugar to make easy to set up some special use-cases like Kubernetes targets, sharding and some New Relic related settings like remote write endpoints. + +The configurator will create [scrape_config](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config), [relabel_config](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config), [remote_write](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#remote_write) and other entries based on the defined configuration. + +As general rules: +- Configs parameters having the same name as the [Prometheus configuration](https://prometheus.io/docs/prometheus/latest/configuration/configuration/) should have similar behavior. For example, the `tls_config` defined inside a `Kubernetes.jobs` will have the same definition as [tls_config](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#tls_config) of Prometheus and will affect all targets scraped by that job. +- Configs starting with `extra_` prefix will be appended to the ones created by the Configurator. For example, the relabel configs defined in `extra_relabel_config` on the Kubernetes section will be appended to the end of the list that is already being generated by the Configurator for filtering, sharding, metadata decoration, etc. + +### Default Kubernetes jobs configuration + +By default, some Kubernetes objects are discovered and scraped by Prometheus. Taking into account the snippet from `values.yaml` below: + +```yaml + integrations_filter: + enabled: true + source_labels: ["app.kubernetes.io/name", "app.newrelic.io/name", "k8s-app"] + app_values: ["redis", "traefik", "calico", "nginx", "coredns", "etcd", "cockroachdb", "velero", "harbor", "argocd"] + jobs: + - job_name_prefix: default + target_discovery: + pod: true + endpoints: true + filter: + annotations: + prometheus.io/scrape: true + - job_name_prefix: newrelic + integrations_filter: + enabled: false + target_discovery: + pod: true + endpoints: true + filter: + annotations: + newrelic.io/scrape: true +``` + +All pods and endpoints with the `newrelic.io/scrape: true` annotation will be scraped by default. + +Moreover, the solution will scrape as well all pods and endpoints with the `prometheus.io/scrape: true` annotations and +having one of the labels matching the integrations_filter configuration. + +Notice that at any point you can turn off the integrations filters and scrape all pods and services annotated with +`prometheus.io/scrape: true` by setting `config.kubernetes.integrations_filter.integrations_filter: false` or turning +it off in any specific job. + +### Kubernetes job examples + +#### API Server metrics +By default, the API Server Service named `kubernetes` is created in the `default` namespace. The following configuration will scrape metrics from all endpoints behind the mentioned service using the Prometheus Pod bearer token as Authorization Header: + +```yaml +config: + kubernetes: + jobs: + - job_name_prefix: apiserver + target_discovery: + endpoints: true + extra_relabel_config: + # Filter endpoints on `default` namespace associated to `kubernetes` service. + - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name] + action: keep + regex: default;kubernetes + + scheme: https + tls_config: + ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + insecure_skip_verify: true + authorization: + credentials_file: /var/run/secrets/kubernetes.io/serviceaccount/token +``` + +### Metrics Filtering + +Check [docs](https://github.com/newrelic/newrelic-prometheus-configurator/blob/main/docs/MetricsFilters.md) for a detailed explanation and examples of how to filter metrics and labels. + +### Self metrics + +By default, it is defined as a job in `static_target.jobs` to obtain self-metrics. Particularly, a snippet like the one +below is used. If you define your own static_targets jobs, it is important to also include this kind of job in order +to keep getting self-metrics. + +```yaml +config: + static_targets: + jobs: + - job_name: self-metrics + targets: + - "localhost:9090" + extra_metric_relabel_config: + - source_labels: [__name__] + regex: "" + action: keep +``` + +### Low data mode + +There are two mechanisms to reduce the amount of data that this integration sends to New Relic. See this snippet from the `values.yaml` file: +```yaml +lowDataMode: false + +config: + common: + scrape_interval: 30s +``` + +You might set `lowDataMode` flag to `true` (it will filter some metrics which can also be collected using New Relic Kubernetes integration), check +`values.yaml` for details. + +It is also possible to adjust how frequently Prometheus scrapes the targets by setting up the` config.common.scrape_interval` value. + +### Affinities and tolerations + +The New Relic common library allows you to set affinities, tolerations, and node selectors globally using e.g. `.global.affinity` to ease the configuration +when you use this chart using `nri-bundle`. This chart has an extra level of granularity to the components that it deploys: +control plane, ksm, and kubelet. + +Take this snippet as an example: +```yaml +global: + affinity: {} +affinity: {} +``` + +The order to set the affinity is to set `affinity` field (at root level), if that value is empty, the chart fallbacks to `global.affinity`. + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| affinity | object | `{}` | Sets pod/node affinities set almost globally. (See [Affinities and tolerations](README.md#affinities-and-tolerations)) | +| cluster | string | `""` | Name of the Kubernetes cluster monitored. Can be configured also with `global.cluster`. Note it will be set as an external label in prometheus configuration, it will have precedence over `config.common.external_labels.cluster_name` and `customAttributes.cluster_name``. | +| config | object | See `values.yaml` | It holds the New Relic Prometheus configuration. Here you can easily set up Prometheus to get set metrics, discover ponds and endpoints Kubernetes and send metrics to New Relic using remote-write. | +| config.common | object | See `values.yaml` | Include global configuration for Prometheus agent. | +| config.common.scrape_interval | string | `"30s"` | How frequently to scrape targets by default, unless a different value is specified on the job. | +| config.extra_remote_write | object | `nil` | It includes additional remote-write configuration. Note this configuration is not parsed, so valid [prometheus remote_write configuration](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#remote_write) should be provided. | +| config.extra_scrape_configs | list | `[]` | It is possible to include extra scrape configuration in [prometheus format](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config). Please note, it should be a valid Prometheus configuration which will not be parsed by the chart. WARNING extra_scrape_configs is a raw Prometheus config. Therefore, the metrics collected thanks to it will not have by default the metadata (pod_name, service_name, ...) added by the configurator for the static or kubernetes jobs. This configuration should be used as a workaround whenever kubernetes and static job do not cover a particular use-case. | +| config.kubernetes | object | See `values.yaml` | It allows defining scrape jobs for Kubernetes in a simple way. | +| config.kubernetes.integrations_filter.app_values | list | `["redis","traefik","calico","nginx","coredns","kube-dns","etcd","cockroachdb","velero","harbor","argocd"]` | app_values used to create the regex used in the relabel config added by the integration filters configuration. Note that a single regex will be created from this list, example: '.*(?i)(app1|app2|app3).*' | +| config.kubernetes.integrations_filter.enabled | bool | `true` | enabling the integration filters, merely the targets having one of the specified labels matching one of the values of app_values are scraped. Each job configuration can override this default. | +| config.kubernetes.integrations_filter.source_labels | list | `["app.kubernetes.io/name","app.newrelic.io/name","k8s-app"]` | source_labels used to fetch label values in the relabel config added by the integration filters configuration | +| config.newrelic_remote_write | object | See `values.yaml` | Newrelic remote-write configuration settings. | +| config.static_targets | object | See `values.yaml`. | It allows defining scrape jobs for targets with static URLs. | +| config.static_targets.jobs | list | See `values.yaml`. | List of static target jobs. By default, it defines a job to get self-metrics. Please note, if you define `static_target.jobs` and would like to keep self-metrics you need to include a job like the one defined by default. | +| containerSecurityContext | object | `{}` | Sets security context (at container level). Can be configured also with `global.containerSecurityContext` | +| customAttributes | object | `{}` | Adds extra attributes to prometheus external labels. Can be configured also with `global.customAttributes`. Please note, values defined in `common.config.externar_labels` will have precedence over `customAttributes`. | +| customSecretLicenseKey | string | `""` | In case you don't want to have the license key in you values, this allows you to point to which secret key is the license key located. Can be configured also with `global.customSecretLicenseKey` | +| customSecretName | string | `""` | In case you don't want to have the license key in you values, this allows you to point to a user created secret to get the key from there. Can be configured also with `global.customSecretName` | +| dnsConfig | object | `{}` | Sets pod's dnsConfig. Can be configured also with `global.dnsConfig` | +| extraVolumeMounts | list | `[]` | Defines where to mount volumes specified with `extraVolumes` | +| extraVolumes | list | `[]` | Volumes to mount in the containers | +| fullnameOverride | string | `""` | Override the full name of the release | +| hostNetwork | bool | `false` | Sets pod's hostNetwork. Can be configured also with `global.hostNetwork` | +| images.configurator | object | See `values.yaml` | Image for New Relic configurator. | +| images.prometheus | object | See `values.yaml` | Image for prometheus which is executed in agent mode. | +| images.pullSecrets | list | `[]` | The secrets that are needed to pull images from a custom registry. | +| labels | object | `{}` | Additional labels for chart objects. Can be configured also with `global.labels` | +| licenseKey | string | `""` | This set this license key to use. Can be configured also with `global.licenseKey` | +| lowDataMode | bool | false | Reduces the number of metrics sent in order to reduce costs. It can be configured also with `global.lowDataMode`. Specifically, it makes Prometheus stop reporting some Kubernetes cluster-specific metrics, you can see details in `static/lowdatamodedefaults.yaml`. | +| metric_type_override | object | `{"enabled":true}` | It holds the configuration for metric type override. If enabled, a series of metric relabel configs will be added to `config.newrelic_remote_write.extra_write_relabel_configs`, you can check the whole list in `static/metrictyperelabeldefaults.yaml` | +| nameOverride | string | `""` | Override the name of the chart | +| nodeSelector | object | `{}` | Sets pod's node selector almost globally. (See [Affinities and tolerations](README.md#affinities-and-tolerations)) | +| nrStaging | bool | `false` | Send the metrics to the staging backend. Requires a valid staging license key. Can be configured also with `global.nrStaging` | +| podAnnotations | object | `{}` | Annotations to be added to all pods created by the integration. | +| podLabels | object | `{}` | Additional labels for chart pods. Can be configured also with `global.podLabels` | +| podSecurityContext | object | `{}` | Sets security context (at pod level). Can be configured also with `global.podSecurityContext` | +| priorityClassName | string | `""` | Sets pod's priorityClassName. Can be configured also with `global.priorityClassName` | +| rbac.create | bool | `true` | Whether the chart should automatically create the RBAC objects required to run. | +| rbac.pspEnabled | bool | `false` | Whether the chart should create Pod Security Policy objects. | +| resources | object | `{}` | Resource limits to be added to all pods created by the integration. | +| serviceAccount | object | See `values.yaml` | Settings controlling ServiceAccount creation. | +| serviceAccount.create | bool | `true` | Whether the chart should automatically create the ServiceAccount objects required to run. | +| sharding | string | See `values.yaml` | Set up Prometheus replicas to allow horizontal scalability. | +| tolerations | list | `[]` | Sets pod's tolerations to node taints almost globally. (See [Affinities and tolerations](README.md#affinities-and-tolerations)) | +| verboseLog | bool | `false` | Sets the debug log to Prometheus and prometheus-configurator or all integrations if it is set globally. Can be configured also with `global.verboseLog` | + +## Maintainers + +* [juanjjaramillo](https://github.com/juanjjaramillo) +* [csongnr](https://github.com/csongnr) +* [dbudziwojskiNR](https://github.com/dbudziwojskiNR) diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/README.md.gotmpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/README.md.gotmpl new file mode 100644 index 0000000000..8738b73297 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/README.md.gotmpl @@ -0,0 +1,209 @@ +{{ template "chart.header" . }} +{{ template "chart.deprecationWarning" . }} + +{{ template "chart.description" . }} + +{{ template "chart.homepageLine" . }} + +# Description + +This chart deploys Prometheus Server in Agent mode configured by the `newrelic-prometheus-configurator`. + +The solution is deployed as a StatefulSet for sharding proposes. +Each Pod will execute the `newrelic-prometheus-configurator` init container which will convert the provided config to a config file in the Prometheus format. Once the init container finishes and saves the config in a shared volume, the container running Prometheus in Agent mode will start. + +```mermaid +graph LR + subgraph pod[Pod] + direction TB + subgraph volume[shared volume] + plain[Prometheus Config] + end + + subgraph init-container[init Container] + configurator[Configurator] --> plain[Prometheus Config] + end + + subgraph container[Main Container] + plain[Prometheus Config] --> prom-agent[Prometheus-Agent] + end + + end + + subgraph configMap + NewRelic-Config --> configurator[Configurator] + end + +classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000; +classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; +classDef pod fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5; +class configurator,init-container,container,prom-agent k8s; +class volume plain; +class pod pod; + +``` + +# Helm installation + +You can install this chart using [`nri-bundle`](https://github.com/newrelic/helm-charts/tree/master/charts/nri-bundle) located in the +[helm-charts repository](https://github.com/newrelic/helm-charts) or directly from this repository by adding this Helm repository: + +```shell +helm repo add newrelic-prometheus https://newrelic.github.io/newrelic-prometheus-configurator +helm upgrade --install newrelic newrelic-prometheus/newrelic-prometheus-agent -f your-custom-values.yaml +``` + +{{ template "chart.sourcesSection" . }} + +## Values managed globally + +This chart implements the [New Relic's common Helm library](https://github.com/newrelic/helm-charts/tree/master/library/common-library) which +means that it honors a wide range of defaults and globals common to most New Relic Helm charts. + +Options that can be defined globally include `affinity`, `nodeSelector`, `tolerations`, `proxy` and others. The full list can be found at +[user's guide of the common library](https://github.com/newrelic/helm-charts/blob/master/library/common-library/README.md). + +## Chart particularities + +### Configuration + +The configuration used is similar to the [Prometheus configuration](https://prometheus.io/docs/prometheus/latest/configuration/configuration/), but it includes some syntactic sugar to make easy to set up some special use-cases like Kubernetes targets, sharding and some New Relic related settings like remote write endpoints. + +The configurator will create [scrape_config](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config), [relabel_config](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config), [remote_write](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#remote_write) and other entries based on the defined configuration. + +As general rules: +- Configs parameters having the same name as the [Prometheus configuration](https://prometheus.io/docs/prometheus/latest/configuration/configuration/) should have similar behavior. For example, the `tls_config` defined inside a `Kubernetes.jobs` will have the same definition as [tls_config](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#tls_config) of Prometheus and will affect all targets scraped by that job. +- Configs starting with `extra_` prefix will be appended to the ones created by the Configurator. For example, the relabel configs defined in `extra_relabel_config` on the Kubernetes section will be appended to the end of the list that is already being generated by the Configurator for filtering, sharding, metadata decoration, etc. + +### Default Kubernetes jobs configuration + +By default, some Kubernetes objects are discovered and scraped by Prometheus. Taking into account the snippet from `values.yaml` below: + +```yaml + integrations_filter: + enabled: true + source_labels: ["app.kubernetes.io/name", "app.newrelic.io/name", "k8s-app"] + app_values: ["redis", "traefik", "calico", "nginx", "coredns", "etcd", "cockroachdb", "velero", "harbor", "argocd"] + jobs: + - job_name_prefix: default + target_discovery: + pod: true + endpoints: true + filter: + annotations: + prometheus.io/scrape: true + - job_name_prefix: newrelic + integrations_filter: + enabled: false + target_discovery: + pod: true + endpoints: true + filter: + annotations: + newrelic.io/scrape: true +``` + +All pods and endpoints with the `newrelic.io/scrape: true` annotation will be scraped by default. + +Moreover, the solution will scrape as well all pods and endpoints with the `prometheus.io/scrape: true` annotations and +having one of the labels matching the integrations_filter configuration. + +Notice that at any point you can turn off the integrations filters and scrape all pods and services annotated with +`prometheus.io/scrape: true` by setting `config.kubernetes.integrations_filter.integrations_filter: false` or turning +it off in any specific job. + +### Kubernetes job examples + +#### API Server metrics +By default, the API Server Service named `kubernetes` is created in the `default` namespace. The following configuration will scrape metrics from all endpoints behind the mentioned service using the Prometheus Pod bearer token as Authorization Header: + +```yaml +config: + kubernetes: + jobs: + - job_name_prefix: apiserver + target_discovery: + endpoints: true + extra_relabel_config: + # Filter endpoints on `default` namespace associated to `kubernetes` service. + - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name] + action: keep + regex: default;kubernetes + + scheme: https + tls_config: + ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + insecure_skip_verify: true + authorization: + credentials_file: /var/run/secrets/kubernetes.io/serviceaccount/token +``` + +### Metrics Filtering + +Check [docs](https://github.com/newrelic/newrelic-prometheus-configurator/blob/main/docs/MetricsFilters.md) for a detailed explanation and examples of how to filter metrics and labels. + +### Self metrics + +By default, it is defined as a job in `static_target.jobs` to obtain self-metrics. Particularly, a snippet like the one +below is used. If you define your own static_targets jobs, it is important to also include this kind of job in order +to keep getting self-metrics. + +```yaml +config: + static_targets: + jobs: + - job_name: self-metrics + targets: + - "localhost:9090" + extra_metric_relabel_config: + - source_labels: [__name__] + regex: "" + action: keep +``` + +### Low data mode + +There are two mechanisms to reduce the amount of data that this integration sends to New Relic. See this snippet from the `values.yaml` file: +```yaml +lowDataMode: false + +config: + common: + scrape_interval: 30s +``` + +You might set `lowDataMode` flag to `true` (it will filter some metrics which can also be collected using New Relic Kubernetes integration), check +`values.yaml` for details. + +It is also possible to adjust how frequently Prometheus scrapes the targets by setting up the` config.common.scrape_interval` value. + + +### Affinities and tolerations + +The New Relic common library allows you to set affinities, tolerations, and node selectors globally using e.g. `.global.affinity` to ease the configuration +when you use this chart using `nri-bundle`. This chart has an extra level of granularity to the components that it deploys: +control plane, ksm, and kubelet. + +Take this snippet as an example: +```yaml +global: + affinity: {} +affinity: {} +``` + +The order to set the affinity is to set `affinity` field (at root level), if that value is empty, the chart fallbacks to `global.affinity`. + +{{ template "chart.valuesSection" . }} + +{{ if .Maintainers }} +## Maintainers +{{ range .Maintainers }} +{{- if .Name }} +{{- if .Url }} +* [{{ .Name }}]({{ .Url }}) +{{- else }} +* {{ .Name }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/.helmignore b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/.helmignore new file mode 100644 index 0000000000..0e8a0eb36f --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/.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 +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/Chart.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/Chart.yaml new file mode 100644 index 0000000000..b65ac15d4c --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/Chart.yaml @@ -0,0 +1,17 @@ +apiVersion: v2 +description: Provides helpers to provide consistency on all the charts +keywords: +- newrelic +- chart-library +maintainers: +- name: juanjjaramillo + url: https://github.com/juanjjaramillo +- name: csongnr + url: https://github.com/csongnr +- name: dbudziwojskiNR + url: https://github.com/dbudziwojskiNR +- name: kang-makes + url: https://github.com/kang-makes +name: common-library +type: library +version: 1.2.0 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/DEVELOPERS.md b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/DEVELOPERS.md new file mode 100644 index 0000000000..3ccc108e2d --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/DEVELOPERS.md @@ -0,0 +1,663 @@ +# Functions/templates documented for chart writers +Here is some rough documentation separated by the file that contains the function, the function +name and how to use it. We are not covering functions that start with `_` (e.g. +`newrelic.common.license._licenseKey`) because they are used internally by this library for +other helpers. Helm does not have the concept of "public" or "private" functions/templates so +this is a convention of ours. + +## _naming.tpl +These functions are used to name objects. + +### `newrelic.common.naming.name` +This is the same as the idiomatic `CHART-NAME.name` that is created when you use `helm create`. + +It honors `.Values.nameOverride`. + +Usage: +```mustache +{{ include "newrelic.common.naming.name" . }} +``` + +### `newrelic.common.naming.fullname` +This is the same as the idiomatic `CHART-NAME.fullname` that is created when you use `helm create` + +It honors `.Values.fullnameOverride`. + +Usage: +```mustache +{{ include "newrelic.common.naming.fullname" . }} +``` + +### `newrelic.common.naming.chart` +This is the same as the idiomatic `CHART-NAME.chart` that is created when you use `helm create`. + +It is mostly useless for chart writers. It is used internally for templating the labels but there +is no reason to keep it "private". + +Usage: +```mustache +{{ include "newrelic.common.naming.chart" . }} +``` + +### `newrelic.common.naming.truncateToDNS` +This is a useful template that could be used to trim a string to 63 chars and does not end with a dash (`-`). +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). + +Usage: +```mustache +{{ $nameToTruncate := "a-really-really-really-really-REALLY-long-string-that-should-be-truncated-because-it-is-enought-long-to-brak-something" +{{- $truncatedName := include "newrelic.common.naming.truncateToDNS" $nameToTruncate }} +{{- $truncatedName }} +{{- /* This should print: a-really-really-really-really-REALLY-long-string-that-should-be */ -}} +``` + +### `newrelic.common.naming.truncateToDNSWithSuffix` +This template function is the same as the above but instead of receiving a string you should give a `dict` +with a `name` and a `suffix`. This function will join them with a dash (`-`) and trim the `name` so the +result of `name-suffix` is no more than 63 chars + +Usage: +```mustache +{{ $nameToTruncate := "a-really-really-really-really-REALLY-long-string-that-should-be-truncated-because-it-is-enought-long-to-brak-something" +{{- $suffix := "A-NOT-SO-LONG-SUFFIX" }} +{{- $truncatedName := include "truncateToDNSWithSuffix" (dict "name" $nameToTruncate "suffix" $suffix) }} +{{- $truncatedName }} +{{- /* This should print: a-really-really-really-really-REALLY-long-A-NOT-SO-LONG-SUFFIX */ -}} +``` + + + +## _labels.tpl +### `newrelic.common.labels`, `newrelic.common.labels.selectorLabels` and `newrelic.common.labels.podLabels` +These are functions that are used to label objects. They are configured by this `values.yaml` +```yaml +global: + podLabels: {} # included in all the pods of all the charts that implement this library + labels: {} # included in all the objects of all the charts that implement this library +podLabels: {} # included in all the pods of this chart +labels: {} # included in all the objects of this chart +``` + +label maps are merged from global to local values. + +And chart writer should use them like this: +```mustache +metadata: + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +spec: + selector: + matchLabels: + {{- include "newrelic.common.labels.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "newrelic.common.labels.podLabels" . | nindent 8 }} +``` + +`newrelic.common.labels.podLabels` includes `newrelic.common.labels.selectorLabels` automatically. + + + +## _priority-class-name.tpl +### `newrelic.common.priorityClassName` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + priorityClassName: "" +priorityClassName: "" +``` + +Be careful: chart writers should put an empty string (or any kind of Helm falsiness) for this +library to work properly. If in your values a non-falsy `priorityClassName` is found, the global +one is going to be always ignored. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.priorityClassName" . }} + priorityClassName: {{ . }} + {{- end }} +``` + + + +## _hostnetwork.tpl +### `newrelic.common.hostNetwork` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + hostNetwork: # Note that this is empty (nil) +hostNetwork: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `hostNetwork` is defined, the global one is going to be always ignored. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.hostNetwork" . }} + hostNetwork: {{ . }} + {{- end }} +``` + +### `newrelic.common.hostNetwork.value` +This function is an abstraction of the function above but this returns directly "true" or "false". + +Be careful with using this with an `if` as Helm does evaluate "false" (string) as `true`. + +Usage (example in a pod spec): +```mustache +spec: + hostNetwork: {{ include "newrelic.common.hostNetwork.value" . }} +``` + + + +## _dnsconfig.tpl +### `newrelic.common.dnsConfig` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + dnsConfig: {} +dnsConfig: {} +``` + +Be careful: chart writers should put an empty string (or any kind of Helm falsiness) for this +library to work properly. If in your values a non-falsy `dnsConfig` is found, the global +one is going to be always ignored. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.dnsConfig" . }} + dnsConfig: + {{- . | nindent 4 }} + {{- end }} +``` + + + +## _images.tpl +These functions help us to deal with how images are templated. This allows setting `registries` +where to fetch images globally while being flexible enough to fit in different maps of images +and deployments with one or more images. This is the example of a complex `values.yaml` that +we are going to use during the documentation of these functions: + +```yaml +global: + images: + registry: nexus-3-instance.internal.clients-domain.tld +jobImage: + registry: # defaults to "example.tld" when empty in these examples + repository: ingress-nginx/kube-webhook-certgen + tag: v1.1.1 + pullPolicy: IfNotPresent + pullSecrets: [] +images: + integration: + registry: + repository: newrelic/nri-kube-events + tag: 1.8.0 + pullPolicy: IfNotPresent + agent: + registry: + repository: newrelic/k8s-events-forwarder + tag: 1.22.0 + pullPolicy: IfNotPresent + pullSecrets: [] +``` + +### `newrelic.common.images.image` +This will return a string with the image ready to be downloaded that includes the registry, the image and the tag. +`defaultRegistry` is used to keep `registry` field empty in `values.yaml` so you can override the image using +`global.images.registry`, your local `jobImage.registry` and be able to fallback to a registry that is not `docker.io` +(Or the default repository that the client could have set in the CRI). + +Usage: +```mustache +{{- /* For the integration */}} +{{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.images.agent "context" .) }} +{{- /* For jobImage */}} +{{ include "newrelic.common.images.image" ( dict "defaultRegistry" "example.tld" "imageRoot" .Values.jobImage "context" .) }} +``` + +### `newrelic.common.images.registry` +It returns the registry from the global or local values. You should avoid using this helper to create your image +URL and use `newrelic.common.images.image` instead, but it is there to be used in case it is needed. + +Usage: +```mustache +{{- /* For the integration */}} +{{ include "newrelic.common.images.registry" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.registry" ( dict "imageRoot" .Values.images.agent "context" .) }} +{{- /* For jobImage */}} +{{ include "newrelic.common.images.registry" ( dict "defaultRegistry" "example.tld" "imageRoot" .Values.jobImage "context" .) }} +``` + +### `newrelic.common.images.repository` +It returns the image from the values. You should avoid using this helper to create your image +URL and use `newrelic.common.images.image` instead, but it is there to be used in case it is needed. + +Usage: +```mustache +{{- /* For jobImage */}} +{{ include "newrelic.common.images.repository" ( dict "imageRoot" .Values.jobImage "context" .) }} +{{- /* For the integration */}} +{{ include "newrelic.common.images.repository" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.repository" ( dict "imageRoot" .Values.images.agent "context" .) }} +``` + +### `newrelic.common.images.tag` +It returns the image's tag from the values. You should avoid using this helper to create your image +URL and use `newrelic.common.images.image` instead, but it is there to be used in case it is needed. + +Usage: +```mustache +{{- /* For jobImage */}} +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.jobImage "context" .) }} +{{- /* For the integration */}} +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.images.agent "context" .) }} +``` + +### `newrelic.common.images.renderPullSecrets` +If returns a merged map that contains the pull secrets from the global configuration and the local one. + +Usage: +```mustache +{{- /* For jobImage */}} +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" .Values.jobImage.pullSecrets "context" .) }} +{{- /* For the integration */}} +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" .Values.images.pullSecrets "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" .Values.images.pullSecrets "context" .) }} +``` + + + +## _serviceaccount.tpl +These functions are used to evaluate if the service account should be created, with which name and add annotations to it. + +The functions that the common library has implemented for service accounts are: +* `newrelic.common.serviceAccount.create` +* `newrelic.common.serviceAccount.name` +* `newrelic.common.serviceAccount.annotations` + +Usage: +```mustache +{{- if include "newrelic.common.serviceAccount.create" . -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + {{- with (include "newrelic.common.serviceAccount.annotations" .) }} + annotations: + {{- . | nindent 4 }} + {{- end }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "newrelic.common.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} +{{- end }} +``` + + + +## _affinity.tpl, _nodeselector.tpl and _tolerations.tpl +These three files are almost the same and they follow the idiomatic way of `helm create`. + +Each function also looks if there is a global value like the other helpers. +```yaml +global: + affinity: {} + nodeSelector: {} + tolerations: [] +affinity: {} +nodeSelector: {} +tolerations: [] +``` + +The values here are replaced instead of be merged. If a value at root level is found, the global one is ignored. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.nodeSelector" . }} + nodeSelector: + {{- . | nindent 4 }} + {{- end }} + {{- with include "newrelic.common.affinity" . }} + affinity: + {{- . | nindent 4 }} + {{- end }} + {{- with include "newrelic.common.tolerations" . }} + tolerations: + {{- . | nindent 4 }} + {{- end }} +``` + + + +## _agent-config.tpl +### `newrelic.common.agentConfig.defaults` +This returns a YAML that the agent can use directly as a config that includes other options from the values file like verbose mode, +custom attributes, FedRAMP and such. + +Usage: +```mustache +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include newrelic.common.naming.truncateToDNSWithSuffix (dict "name" (include "newrelic.common.naming.fullname" .) suffix "agent-config") }} + namespace: {{ .Release.Namespace }} +data: + newrelic-infra.yml: |- + # This is the configuration file for the infrastructure agent. See: + # https://docs.newrelic.com/docs/infrastructure/install-infrastructure-agent/configuration/infrastructure-agent-configuration-settings/ + {{- include "newrelic.common.agentConfig.defaults" . | nindent 4 }} +``` + + + +## _cluster.tpl +### `newrelic.common.cluster` +Returns the cluster name + +Usage: +```mustache +{{ include "newrelic.common.cluster" . }} +``` + + + +## _custom-attributes.tpl +### `newrelic.common.customAttributes` +Return custom attributes in YAML format. + +Usage: +```mustache +apiVersion: v1 +kind: ConfigMap +metadata: + name: example +data: + custom-attributes.yaml: | + {{- include "newrelic.common.customAttributes" . | nindent 4 }} + custom-attributes.json: | + {{- include "newrelic.common.customAttributes" . | fromYaml | toJson | nindent 4 }} +``` + + + +## _fedramp.tpl +### `newrelic.common.fedramp.enabled` +Returns true if FedRAMP is enabled or an empty string if not. It can be safely used in conditionals as an empty string is a Helm falsiness. + +Usage: +```mustache +{{ include "newrelic.common.fedramp.enabled" . }} +``` + +### `newrelic.common.fedramp.enabled.value` +Returns true if FedRAMP is enabled or false if not. This is to have the value of FedRAMP ready to be templated. + +Usage: +```mustache +{{ include "newrelic.common.fedramp.enabled.value" . }} +``` + + + +## _license.tpl +### `newrelic.common.license.secretName` and ### `newrelic.common.license.secretKeyName` +Returns the secret and key inside the secret where to read the license key. + +The common library will take care of using a user-provided custom secret or creating a secret that contains the license key. + +To create the secret use `newrelic.common.license.secret`. + +Usage: +```mustache +{{- if and (.Values.controlPlane.enabled) (not (include "newrelic.fargate" .)) }} +apiVersion: v1 +kind: Pod +metadata: + name: example +spec: + containers: + - name: agent + env: + - name: "NRIA_LICENSE_KEY" + valueFrom: + secretKeyRef: + name: {{ include "newrelic.common.license.secretName" . }} + key: {{ include "newrelic.common.license.secretKeyName" . }} +``` + + + +## _license_secret.tpl +### `newrelic.common.license.secret` +This function templates the secret that is used by agents and integrations with the license Key provided by the user. It will +template nothing (empty string) if the user provides a custom pair of secret name and key. + +This template also fails in case the user has not provided any license key or custom secret so no safety checks have to be done +by chart writers. + +You just must have a template with these two lines: +```mustache +{{- /* Common library will take care of creating the secret or not. */ -}} +{{- include "newrelic.common.license.secret" . -}} +``` + + + +## _insights.tpl +### `newrelic.common.insightsKey.secretName` and ### `newrelic.common.insightsKey.secretKeyName` +Returns the secret and key inside the secret where to read the insights key. + +The common library will take care of using a user-provided custom secret or creating a secret that contains the insights key. + +To create the secret use `newrelic.common.insightsKey.secret`. + +Usage: +```mustache +apiVersion: v1 +kind: Pod +metadata: + name: statsd +spec: + containers: + - name: statsd + env: + - name: "INSIGHTS_KEY" + valueFrom: + secretKeyRef: + name: {{ include "newrelic.common.insightsKey.secretName" . }} + key: {{ include "newrelic.common.insightsKey.secretKeyName" . }} +``` + + + +## _insights_secret.tpl +### `newrelic.common.insightsKey.secret` +This function templates the secret that is used by agents and integrations with the insights key provided by the user. It will +template nothing (empty string) if the user provides a custom pair of secret name and key. + +This template also fails in case the user has not provided any insights key or custom secret so no safety checks have to be done +by chart writers. + +You just must have a template with these two lines: +```mustache +{{- /* Common library will take care of creating the secret or not. */ -}} +{{- include "newrelic.common.insightsKey.secret" . -}} +``` + + + +## _low-data-mode.tpl +### `newrelic.common.lowDataMode` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + lowDataMode: # Note that this is empty (nil) +lowDataMode: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `lowdataMode` is defined, the global one is going to be always ignored. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage: +```mustache +{{ include "newrelic.common.lowDataMode" . }} +``` + + + +## _privileged.tpl +### `newrelic.common.privileged` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + privileged: # Note that this is empty (nil) +privileged: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `privileged` is defined, the global one is going to be always ignored. + +Chart writers could override this and put directly a `true` in the `values.yaml` to override the +default of the common library. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage: +```mustache +{{ include "newrelic.common.privileged" . }} +``` + +### `newrelic.common.privileged.value` +Returns true if privileged mode is enabled or false if not. This is to have the value of privileged ready to be templated. + +Usage: +```mustache +{{ include "newrelic.common.privileged.value" . }} +``` + + + +## _proxy.tpl +### `newrelic.common.proxy` +Returns the proxy URL configured by the user. + +Usage: +```mustache +{{ include "newrelic.common.proxy" . }} +``` + + + +## _security-context.tpl +Use these functions to share the security context among all charts. Useful in clusters that have security enforcing not to +use the root user (like OpenShift) or users that have an admission webhooks. + +The functions are: +* `newrelic.common.securityContext.container` +* `newrelic.common.securityContext.pod` + +Usage: +```mustache +apiVersion: v1 +kind: Pod +metadata: + name: example +spec: + spec: + {{- with include "newrelic.common.securityContext.pod" . }} + securityContext: + {{- . | nindent 8 }} + {{- end }} + + containers: + - name: example + {{- with include "nriKubernetes.securityContext.container" . }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} +``` + + + +## _staging.tpl +### `newrelic.common.nrStaging` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + nrStaging: # Note that this is empty (nil) +nrStaging: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `nrStaging` is defined, the global one is going to be always ignored. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage: +```mustache +{{ include "newrelic.common.nrStaging" . }} +``` + +### `newrelic.common.nrStaging.value` +Returns true if staging is enabled or false if not. This is to have the staging value ready to be templated. + +Usage: +```mustache +{{ include "newrelic.common.nrStaging.value" . }} +``` + + + +## _verbose-log.tpl +### `newrelic.common.verboseLog` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + verboseLog: # Note that this is empty (nil) +verboseLog: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `verboseLog` is defined, the global one is going to be always ignored. + +Usage: +```mustache +{{ include "newrelic.common.verboseLog" . }} +``` + +### `newrelic.common.verboseLog.valueAsBoolean` +Returns true if verbose is enabled or false if not. This is to have the verbose value ready to be templated as a boolean + +Usage: +```mustache +{{ include "newrelic.common.verboseLog.valueAsBoolean" . }} +``` + +### `newrelic.common.verboseLog.valueAsInt` +Returns 1 if verbose is enabled or 0 if not. This is to have the verbose value ready to be templated as an integer + +Usage: +```mustache +{{ include "newrelic.common.verboseLog.valueAsInt" . }} +``` diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/README.md b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/README.md new file mode 100644 index 0000000000..10f08ca677 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/README.md @@ -0,0 +1,106 @@ +# Helm Common library + +The common library is a way to unify the UX through all the Helm charts that implement it. + +The tooling suite that New Relic is huge and growing and this allows to set things globally +and locally for a single chart. + +## Documentation for chart writers + +If you are writing a chart that is going to use this library you can check the [developers guide](/library/common-library/DEVELOPERS.md) to see all +the functions/templates that we have implemented, what they do and how to use them. + +## Values managed globally + +We want to have a seamless experience through all the charts so we created this library that tries to standardize the behaviour +of all the charts. Sadly, because of the complexity of all these integrations, not all the charts behave exactly as expected. + +An example is `newrelic-infrastructure` that ignores `hostNetwork` in the control plane scraper because most of the users has the +control plane listening in the node to `localhost`. + +For each chart that has a special behavior (or further information of the behavior) there is a "chart particularities" section +in its README.md that explains which is the expected behavior. + +At the time of writing this, all the charts from `nri-bundle` except `newrelic-logging` and `synthetics-minion` implements this +library and honors global options as described in this document. + +Here is a list of global options: + +| Global keys | Local keys | Default | Merged[1](#values-managed-globally-1) | Description | +|-------------|------------|---------|--------------------------------------------------|-------------| +| global.cluster | cluster | `""` | | Name of the Kubernetes cluster monitored | +| global.licenseKey | licenseKey | `""` | | This set this license key to use | +| global.customSecretName | customSecretName | `""` | | In case you don't want to have the license key in you values, this allows you to point to a user created secret to get the key from there | +| global.customSecretLicenseKey | customSecretLicenseKey | `""` | | In case you don't want to have the license key in you values, this allows you to point to which secret key is the license key located | +| global.podLabels | podLabels | `{}` | yes | Additional labels for chart pods | +| global.labels | labels | `{}` | yes | Additional labels for chart objects | +| global.priorityClassName | priorityClassName | `""` | | Sets pod's priorityClassName | +| global.hostNetwork | hostNetwork | `false` | | Sets pod's hostNetwork | +| global.dnsConfig | dnsConfig | `{}` | | Sets pod's dnsConfig | +| global.images.registry | See [Further information](#values-managed-globally-2) | `""` | | Changes the registry where to get the images. Useful when there is an internal image cache/proxy | +| global.images.pullSecrets | See [Further information](#values-managed-globally-2) | `[]` | yes | Set secrets to be able to fetch images | +| global.podSecurityContext | podSecurityContext | `{}` | | Sets security context (at pod level) | +| global.containerSecurityContext | containerSecurityContext | `{}` | | Sets security context (at container level) | +| global.affinity | affinity | `{}` | | Sets pod/node affinities | +| global.nodeSelector | nodeSelector | `{}` | | Sets pod's node selector | +| global.tolerations | tolerations | `[]` | | Sets pod's tolerations to node taints | +| global.serviceAccount.create | serviceAccount.create | `true` | | Configures if the service account should be created or not | +| global.serviceAccount.name | serviceAccount.name | name of the release | | Change the name of the service account. This is honored if you disable on this cahrt the creation of the service account so you can use your own. | +| global.serviceAccount.annotations | serviceAccount.annotations | `{}` | yes | Add these annotations to the service account we create | +| global.customAttributes | customAttributes | `{}` | | Adds extra attributes to the cluster and all the metrics emitted to the backend | +| global.fedramp | fedramp | `false` | | Enables FedRAMP | +| global.lowDataMode | lowDataMode | `false` | | Reduces number of metrics sent in order to reduce costs | +| global.privileged | privileged | Depends on the chart | | In each integration it has different behavior. See [Further information](#values-managed-globally-3) but all aims to send less metrics to the backend to try to save costs | +| global.proxy | proxy | `""` | | Configures the integration to send all HTTP/HTTPS request through the proxy in that URL. The URL should have a standard format like `https://user:password@hostname:port` | +| global.nrStaging | nrStaging | `false` | | Send the metrics to the staging backend. Requires a valid staging license key | +| global.verboseLog | verboseLog | `false` | | Sets the debug/trace logs to this integration or all integrations if it is set globally | + +### Further information + +#### 1. Merged + +Merged means that the values from global are not replaced by the local ones. Think in this example: +```yaml +global: + labels: + global: global + hostNetwork: true + nodeSelector: + global: global + +labels: + local: local +nodeSelector: + local: local +hostNetwork: false +``` + +This values will template `hostNetwork` to `false`, a map of labels `{ "global": "global", "local": "local" }` and a `nodeSelector` with +`{ "local": "local" }`. + +As Helm by default merges all the maps it could be confusing that we have two behaviors (merging `labels` and replacing `nodeSelector`) +the `values` from global to local. This is the rationale behind this: +* `hostNetwork` is templated to `false` because is overriding the value defined globally. +* `labels` are merged because the user may want to label all the New Relic pods at once and label other solution pods differently for + clarity' sake. +* `nodeSelector` does not merge as `labels` because could make it harder to overwrite/delete a selector that comes from global because + of the logic that Helm follows merging maps. + + +#### 2. Fine grain registries + +Some charts only have 1 image while others that can have 2 or more images. The local path for the registry can change depending +on the chart itself. + +As this is mostly unique per helm chart, you should take a look to the chart's values table (or directly to the `values.yaml` file to see all the +images that you can change. + +This should only be needed if you have an advanced setup that forces you to have granularity enough to force a proxy/cache registry per integration. + + + +#### 3. Privileged mode + +By default, from the common library, the privileged mode is set to false. But most of the helm charts require this to be true to fetch more +metrics so could see a true in some charts. The consequences of the privileged mode differ from one chart to another so for each chart that +honors the privileged mode toggle should be a section in the README explaining which is the behavior with it enabled or disabled. diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_affinity.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_affinity.tpl new file mode 100644 index 0000000000..1b2636754e --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_affinity.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod affinity */ -}} +{{- define "newrelic.common.affinity" -}} + {{- if .Values.affinity -}} + {{- toYaml .Values.affinity -}} + {{- else if .Values.global -}} + {{- if .Values.global.affinity -}} + {{- toYaml .Values.global.affinity -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_agent-config.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_agent-config.tpl new file mode 100644 index 0000000000..9c32861a02 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_agent-config.tpl @@ -0,0 +1,26 @@ +{{/* +This helper should return the defaults that all agents should have +*/}} +{{- define "newrelic.common.agentConfig.defaults" -}} +{{- if include "newrelic.common.verboseLog" . }} +log: + level: trace +{{- end }} + +{{- if (include "newrelic.common.nrStaging" . ) }} +staging: true +{{- end }} + +{{- with include "newrelic.common.proxy" . }} +proxy: {{ . | quote }} +{{- end }} + +{{- with include "newrelic.common.fedramp.enabled" . }} +fedramp: {{ . }} +{{- end }} + +{{- with fromYaml ( include "newrelic.common.customAttributes" . ) }} +custom_attributes: + {{- toYaml . | nindent 2 }} +{{- end }} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_cluster.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_cluster.tpl new file mode 100644 index 0000000000..0197dd35a3 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_cluster.tpl @@ -0,0 +1,15 @@ +{{/* +Return the cluster +*/}} +{{- define "newrelic.common.cluster" -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} + +{{- if .Values.cluster -}} + {{- .Values.cluster -}} +{{- else if $global.cluster -}} + {{- $global.cluster -}} +{{- else -}} + {{ fail "There is not cluster name definition set neither in `.global.cluster' nor `.cluster' in your values.yaml. Cluster name is required." }} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_custom-attributes.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_custom-attributes.tpl new file mode 100644 index 0000000000..92020719c3 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_custom-attributes.tpl @@ -0,0 +1,17 @@ +{{/* +This will render custom attributes as a YAML ready to be templated or be used with `fromYaml`. +*/}} +{{- define "newrelic.common.customAttributes" -}} +{{- $customAttributes := dict -}} + +{{- $global := index .Values "global" | default dict -}} +{{- if $global.customAttributes -}} +{{- $customAttributes = mergeOverwrite $customAttributes $global.customAttributes -}} +{{- end -}} + +{{- if .Values.customAttributes -}} +{{- $customAttributes = mergeOverwrite $customAttributes .Values.customAttributes -}} +{{- end -}} + +{{- toYaml $customAttributes -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_dnsconfig.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_dnsconfig.tpl new file mode 100644 index 0000000000..d4e40aa8af --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_dnsconfig.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod dnsConfig */ -}} +{{- define "newrelic.common.dnsConfig" -}} + {{- if .Values.dnsConfig -}} + {{- toYaml .Values.dnsConfig -}} + {{- else if .Values.global -}} + {{- if .Values.global.dnsConfig -}} + {{- toYaml .Values.global.dnsConfig -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_fedramp.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_fedramp.tpl new file mode 100644 index 0000000000..9df8d6b5e9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_fedramp.tpl @@ -0,0 +1,25 @@ +{{- /* Defines the fedRAMP flag */ -}} +{{- define "newrelic.common.fedramp.enabled" -}} + {{- if .Values.fedramp -}} + {{- if .Values.fedramp.enabled -}} + {{- .Values.fedramp.enabled -}} + {{- end -}} + {{- else if .Values.global -}} + {{- if .Values.global.fedramp -}} + {{- if .Values.global.fedramp.enabled -}} + {{- .Values.global.fedramp.enabled -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} + + + +{{- /* Return FedRAMP value directly ready to be templated */ -}} +{{- define "newrelic.common.fedramp.enabled.value" -}} +{{- if include "newrelic.common.fedramp.enabled" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_hostnetwork.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_hostnetwork.tpl new file mode 100644 index 0000000000..4cf017ef7e --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_hostnetwork.tpl @@ -0,0 +1,39 @@ +{{- /* +Abstraction of the hostNetwork toggle. +This helper allows to override the global `.global.hostNetwork` with the value of `.hostNetwork`. +Returns "true" if `hostNetwork` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.hostNetwork" -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} + +{{- /* +`get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs + +We also want only to return when this is true, returning `false` here will template "false" (string) when doing +an `(include "newrelic.common.hostNetwork" .)`, which is not an "empty string" so it is `true` if it is used +as an evaluation somewhere else. +*/ -}} +{{- if get .Values "hostNetwork" | kindIs "bool" -}} + {{- if .Values.hostNetwork -}} + {{- .Values.hostNetwork -}} + {{- end -}} +{{- else if get $global "hostNetwork" | kindIs "bool" -}} + {{- if $global.hostNetwork -}} + {{- $global.hostNetwork -}} + {{- end -}} +{{- end -}} +{{- end -}} + + +{{- /* +Abstraction of the hostNetwork toggle. +This helper abstracts the function "newrelic.common.hostNetwork" to return true or false directly. +*/ -}} +{{- define "newrelic.common.hostNetwork.value" -}} +{{- if include "newrelic.common.hostNetwork" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_images.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_images.tpl new file mode 100644 index 0000000000..d4fb432905 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_images.tpl @@ -0,0 +1,94 @@ +{{- /* +Return the proper image name +{{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.path.to.the.image "defaultRegistry" "your.private.registry.tld" "context" .) }} +*/ -}} +{{- define "newrelic.common.images.image" -}} + {{- $registryName := include "newrelic.common.images.registry" ( dict "imageRoot" .imageRoot "defaultRegistry" .defaultRegistry "context" .context ) -}} + {{- $repositoryName := include "newrelic.common.images.repository" .imageRoot -}} + {{- $tag := include "newrelic.common.images.tag" ( dict "imageRoot" .imageRoot "context" .context) -}} + + {{- if $registryName -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag | quote -}} + {{- else -}} + {{- printf "%s:%s" $repositoryName $tag | quote -}} + {{- end -}} +{{- end -}} + + + +{{- /* +Return the proper image registry +{{ include "newrelic.common.images.registry" ( dict "imageRoot" .Values.path.to.the.image "defaultRegistry" "your.private.registry.tld" "context" .) }} +*/ -}} +{{- define "newrelic.common.images.registry" -}} +{{- $globalRegistry := "" -}} +{{- if .context.Values.global -}} + {{- if .context.Values.global.images -}} + {{- with .context.Values.global.images.registry -}} + {{- $globalRegistry = . -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- $localRegistry := "" -}} +{{- if .imageRoot.registry -}} + {{- $localRegistry = .imageRoot.registry -}} +{{- end -}} + +{{- $registry := $localRegistry | default $globalRegistry | default .defaultRegistry -}} +{{- if $registry -}} + {{- $registry -}} +{{- end -}} +{{- end -}} + + + +{{- /* +Return the proper image repository +{{ include "newrelic.common.images.repository" .Values.path.to.the.image }} +*/ -}} +{{- define "newrelic.common.images.repository" -}} + {{- .repository -}} +{{- end -}} + + + +{{- /* +Return the proper image tag +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.path.to.the.image "context" .) }} +*/ -}} +{{- define "newrelic.common.images.tag" -}} + {{- .imageRoot.tag | default .context.Chart.AppVersion | toString -}} +{{- end -}} + + + +{{- /* +Return the proper Image Pull Registry Secret Names evaluating values as templates +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" (list .Values.path.to.the.images.pullSecrets1, .Values.path.to.the.images.pullSecrets2) "context" .) }} +*/ -}} +{{- define "newrelic.common.images.renderPullSecrets" -}} + {{- $flatlist := list }} + + {{- if .context.Values.global -}} + {{- if .context.Values.global.images -}} + {{- if .context.Values.global.images.pullSecrets -}} + {{- range .context.Values.global.images.pullSecrets -}} + {{- $flatlist = append $flatlist . -}} + {{- end -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- range .pullSecrets -}} + {{- if not (empty .) -}} + {{- range . -}} + {{- $flatlist = append $flatlist . -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- if $flatlist -}} + {{- toYaml $flatlist -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_insights.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_insights.tpl new file mode 100644 index 0000000000..895c377326 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_insights.tpl @@ -0,0 +1,56 @@ +{{/* +Return the name of the secret holding the Insights Key. +*/}} +{{- define "newrelic.common.insightsKey.secretName" -}} +{{- $default := include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "insightskey" ) -}} +{{- include "newrelic.common.insightsKey._customSecretName" . | default $default -}} +{{- end -}} + +{{/* +Return the name key for the Insights Key inside the secret. +*/}} +{{- define "newrelic.common.insightsKey.secretKeyName" -}} +{{- include "newrelic.common.insightsKey._customSecretKey" . | default "insightsKey" -}} +{{- end -}} + +{{/* +Return local insightsKey if set, global otherwise. +This helper is for internal use. +*/}} +{{- define "newrelic.common.insightsKey._licenseKey" -}} +{{- if .Values.insightsKey -}} + {{- .Values.insightsKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.insightsKey -}} + {{- .Values.global.insightsKey -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name of the secret holding the Insights Key. +This helper is for internal use. +*/}} +{{- define "newrelic.common.insightsKey._customSecretName" -}} +{{- if .Values.customInsightsKeySecretName -}} + {{- .Values.customInsightsKeySecretName -}} +{{- else if .Values.global -}} + {{- if .Values.global.customInsightsKeySecretName -}} + {{- .Values.global.customInsightsKeySecretName -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name key for the Insights Key inside the secret. +This helper is for internal use. +*/}} +{{- define "newrelic.common.insightsKey._customSecretKey" -}} +{{- if .Values.customInsightsKeySecretKey -}} + {{- .Values.customInsightsKeySecretKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.customInsightsKeySecretKey }} + {{- .Values.global.customInsightsKeySecretKey -}} + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_insights_secret.yaml.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_insights_secret.yaml.tpl new file mode 100644 index 0000000000..556caa6ca6 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_insights_secret.yaml.tpl @@ -0,0 +1,21 @@ +{{/* +Renders the insights key secret if user has not specified a custom secret. +*/}} +{{- define "newrelic.common.insightsKey.secret" }} +{{- if not (include "newrelic.common.insightsKey._customSecretName" .) }} +{{- /* Fail if licenseKey is empty and required: */ -}} +{{- if not (include "newrelic.common.insightsKey._licenseKey" .) }} + {{- fail "You must specify a insightsKey or a customInsightsSecretName containing it" }} +{{- end }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "newrelic.common.insightsKey.secretName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +data: + {{ include "newrelic.common.insightsKey.secretKeyName" . }}: {{ include "newrelic.common.insightsKey._licenseKey" . | b64enc }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_labels.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_labels.tpl new file mode 100644 index 0000000000..b025948285 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_labels.tpl @@ -0,0 +1,54 @@ +{{/* +This will render the labels that should be used in all the manifests used by the helm chart. +*/}} +{{- define "newrelic.common.labels" -}} +{{- $global := index .Values "global" | default dict -}} + +{{- $chart := dict "helm.sh/chart" (include "newrelic.common.naming.chart" . ) -}} +{{- $managedBy := dict "app.kubernetes.io/managed-by" .Release.Service -}} +{{- $selectorLabels := fromYaml (include "newrelic.common.labels.selectorLabels" . ) -}} + +{{- $labels := mustMergeOverwrite $chart $managedBy $selectorLabels -}} +{{- if .Chart.AppVersion -}} +{{- $labels = mustMergeOverwrite $labels (dict "app.kubernetes.io/version" .Chart.AppVersion) -}} +{{- end -}} + +{{- $globalUserLabels := $global.labels | default dict -}} +{{- $localUserLabels := .Values.labels | default dict -}} + +{{- $labels = mustMergeOverwrite $labels $globalUserLabels $localUserLabels -}} + +{{- toYaml $labels -}} +{{- end -}} + + + +{{/* +This will render the labels that should be used in deployments/daemonsets template pods as a selector. +*/}} +{{- define "newrelic.common.labels.selectorLabels" -}} +{{- $name := dict "app.kubernetes.io/name" ( include "newrelic.common.naming.name" . ) -}} +{{- $instance := dict "app.kubernetes.io/instance" .Release.Name -}} + +{{- $selectorLabels := mustMergeOverwrite $name $instance -}} + +{{- toYaml $selectorLabels -}} +{{- end }} + + + +{{/* +Pod labels +*/}} +{{- define "newrelic.common.labels.podLabels" -}} +{{- $selectorLabels := fromYaml (include "newrelic.common.labels.selectorLabels" . ) -}} + +{{- $global := index .Values "global" | default dict -}} +{{- $globalPodLabels := $global.podLabels | default dict }} + +{{- $localPodLabels := .Values.podLabels | default dict }} + +{{- $podLabels := mustMergeOverwrite $selectorLabels $globalPodLabels $localPodLabels -}} + +{{- toYaml $podLabels -}} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_license.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_license.tpl new file mode 100644 index 0000000000..647b4ff439 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_license.tpl @@ -0,0 +1,56 @@ +{{/* +Return the name of the secret holding the License Key. +*/}} +{{- define "newrelic.common.license.secretName" -}} +{{- $default := include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "license" ) -}} +{{- include "newrelic.common.license._customSecretName" . | default $default -}} +{{- end -}} + +{{/* +Return the name key for the License Key inside the secret. +*/}} +{{- define "newrelic.common.license.secretKeyName" -}} +{{- include "newrelic.common.license._customSecretKey" . | default "licenseKey" -}} +{{- end -}} + +{{/* +Return local licenseKey if set, global otherwise. +This helper is for internal use. +*/}} +{{- define "newrelic.common.license._licenseKey" -}} +{{- if .Values.licenseKey -}} + {{- .Values.licenseKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.licenseKey -}} + {{- .Values.global.licenseKey -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name of the secret holding the License Key. +This helper is for internal use. +*/}} +{{- define "newrelic.common.license._customSecretName" -}} +{{- if .Values.customSecretName -}} + {{- .Values.customSecretName -}} +{{- else if .Values.global -}} + {{- if .Values.global.customSecretName -}} + {{- .Values.global.customSecretName -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name key for the License Key inside the secret. +This helper is for internal use. +*/}} +{{- define "newrelic.common.license._customSecretKey" -}} +{{- if .Values.customSecretLicenseKey -}} + {{- .Values.customSecretLicenseKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.customSecretLicenseKey }} + {{- .Values.global.customSecretLicenseKey -}} + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_license_secret.yaml.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_license_secret.yaml.tpl new file mode 100644 index 0000000000..610a0a3370 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_license_secret.yaml.tpl @@ -0,0 +1,21 @@ +{{/* +Renders the license key secret if user has not specified a custom secret. +*/}} +{{- define "newrelic.common.license.secret" }} +{{- if not (include "newrelic.common.license._customSecretName" .) }} +{{- /* Fail if licenseKey is empty and required: */ -}} +{{- if not (include "newrelic.common.license._licenseKey" .) }} + {{- fail "You must specify a licenseKey or a customSecretName containing it" }} +{{- end }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "newrelic.common.license.secretName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +data: + {{ include "newrelic.common.license.secretKeyName" . }}: {{ include "newrelic.common.license._licenseKey" . | b64enc }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_low-data-mode.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_low-data-mode.tpl new file mode 100644 index 0000000000..3dd55ef2ff --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_low-data-mode.tpl @@ -0,0 +1,26 @@ +{{- /* +Abstraction of the lowDataMode toggle. +This helper allows to override the global `.global.lowDataMode` with the value of `.lowDataMode`. +Returns "true" if `lowDataMode` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.lowDataMode" -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if (get .Values "lowDataMode" | kindIs "bool") -}} + {{- if .Values.lowDataMode -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.lowDataMode" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.lowDataMode -}} + {{- end -}} +{{- else -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "lowDataMode" | kindIs "bool" -}} + {{- if $global.lowDataMode -}} + {{- $global.lowDataMode -}} + {{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_naming.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_naming.tpl new file mode 100644 index 0000000000..19fa92648c --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_naming.tpl @@ -0,0 +1,73 @@ +{{/* +This is an function to be called directly with a string just to truncate strings to +63 chars because some Kubernetes name fields are limited to that. +*/}} +{{- define "newrelic.common.naming.truncateToDNS" -}} +{{- . | trunc 63 | trimSuffix "-" }} +{{- end }} + + + +{{- /* +Given a name and a suffix returns a 'DNS Valid' which always include the suffix, truncating the name if needed. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If suffix is too long it gets truncated but it always takes precedence over name, so a 63 chars suffix would suppress the name. +Usage: +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" "" "suffix" "my-suffix" ) }} +*/ -}} +{{- define "newrelic.common.naming.truncateToDNSWithSuffix" -}} +{{- $suffix := (include "newrelic.common.naming.truncateToDNS" .suffix) -}} +{{- $maxLen := (max (sub 63 (add1 (len $suffix))) 0) -}} {{- /* We prepend "-" to the suffix so an additional character is needed */ -}} + +{{- $newName := .name | trunc ($maxLen | int) | trimSuffix "-" -}} +{{- if $newName -}} +{{- printf "%s-%s" $newName $suffix -}} +{{- else -}} +{{ $suffix }} +{{- end -}} + +{{- end -}} + + + +{{/* +Expand the name of the chart. +Uses the Chart name by default if nameOverride is not set. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "newrelic.common.naming.name" -}} +{{- $name := .Values.nameOverride | default .Chart.Name -}} +{{- include "newrelic.common.naming.truncateToDNS" $name -}} +{{- end }} + + + +{{/* +Create a default fully qualified app name. +By default the full name will be "" just in if it has the chart name included in that, if not +it will be concatenated like "-". This could change if fullnameOverride or +nameOverride are set. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "newrelic.common.naming.fullname" -}} +{{- $name := include "newrelic.common.naming.name" . -}} + +{{- if .Values.fullnameOverride -}} + {{- $name = .Values.fullnameOverride -}} +{{- else if not (contains $name .Release.Name) -}} + {{- $name = printf "%s-%s" .Release.Name $name -}} +{{- end -}} + +{{- include "newrelic.common.naming.truncateToDNS" $name -}} + +{{- end -}} + + + +{{/* +Create chart name and version as used by the chart label. +This function should not be used for naming objects. Use "common.naming.{name,fullname}" instead. +*/}} +{{- define "newrelic.common.naming.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_nodeselector.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_nodeselector.tpl new file mode 100644 index 0000000000..d488873412 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_nodeselector.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod nodeSelector */ -}} +{{- define "newrelic.common.nodeSelector" -}} + {{- if .Values.nodeSelector -}} + {{- toYaml .Values.nodeSelector -}} + {{- else if .Values.global -}} + {{- if .Values.global.nodeSelector -}} + {{- toYaml .Values.global.nodeSelector -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_priority-class-name.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_priority-class-name.tpl new file mode 100644 index 0000000000..50182b7343 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_priority-class-name.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the pod priorityClassName */ -}} +{{- define "newrelic.common.priorityClassName" -}} + {{- if .Values.priorityClassName -}} + {{- .Values.priorityClassName -}} + {{- else if .Values.global -}} + {{- if .Values.global.priorityClassName -}} + {{- .Values.global.priorityClassName -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_privileged.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_privileged.tpl new file mode 100644 index 0000000000..f3ae814dd9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_privileged.tpl @@ -0,0 +1,28 @@ +{{- /* +This is a helper that returns whether the chart should assume the user is fine deploying privileged pods. +*/ -}} +{{- define "newrelic.common.privileged" -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists. */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if get .Values "privileged" | kindIs "bool" -}} + {{- if .Values.privileged -}} + {{- .Values.privileged -}} + {{- end -}} +{{- else if get $global "privileged" | kindIs "bool" -}} + {{- if $global.privileged -}} + {{- $global.privileged -}} + {{- end -}} +{{- end -}} +{{- end -}} + + + +{{- /* Return directly "true" or "false" based in the exist of "newrelic.common.privileged" */ -}} +{{- define "newrelic.common.privileged.value" -}} +{{- if include "newrelic.common.privileged" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_proxy.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_proxy.tpl new file mode 100644 index 0000000000..60f34c7ec1 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_proxy.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the proxy */ -}} +{{- define "newrelic.common.proxy" -}} + {{- if .Values.proxy -}} + {{- .Values.proxy -}} + {{- else if .Values.global -}} + {{- if .Values.global.proxy -}} + {{- .Values.global.proxy -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_security-context.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_security-context.tpl new file mode 100644 index 0000000000..9edfcabfd0 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_security-context.tpl @@ -0,0 +1,23 @@ +{{- /* Defines the container securityContext context */ -}} +{{- define "newrelic.common.securityContext.container" -}} +{{- $global := index .Values "global" | default dict -}} + +{{- if .Values.containerSecurityContext -}} + {{- toYaml .Values.containerSecurityContext -}} +{{- else if $global.containerSecurityContext -}} + {{- toYaml $global.containerSecurityContext -}} +{{- end -}} +{{- end -}} + + + +{{- /* Defines the pod securityContext context */ -}} +{{- define "newrelic.common.securityContext.pod" -}} +{{- $global := index .Values "global" | default dict -}} + +{{- if .Values.podSecurityContext -}} + {{- toYaml .Values.podSecurityContext -}} +{{- else if $global.podSecurityContext -}} + {{- toYaml $global.podSecurityContext -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_serviceaccount.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_serviceaccount.tpl new file mode 100644 index 0000000000..2d352f6ea9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_serviceaccount.tpl @@ -0,0 +1,90 @@ +{{- /* Defines if the service account has to be created or not */ -}} +{{- define "newrelic.common.serviceAccount.create" -}} +{{- $valueFound := false -}} + +{{- /* Look for a global creation of a service account */ -}} +{{- if get .Values "serviceAccount" | kindIs "map" -}} + {{- if (get .Values.serviceAccount "create" | kindIs "bool") -}} + {{- $valueFound = true -}} + {{- if .Values.serviceAccount.create -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.serviceAccount.name" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.serviceAccount.create -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- /* Look for a local creation of a service account */ -}} +{{- if not $valueFound -}} + {{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} + {{- $global := index .Values "global" | default dict -}} + {{- if get $global "serviceAccount" | kindIs "map" -}} + {{- if get $global.serviceAccount "create" | kindIs "bool" -}} + {{- $valueFound = true -}} + {{- if $global.serviceAccount.create -}} + {{- $global.serviceAccount.create -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- /* In case no serviceAccount value has been found, default to "true" */ -}} +{{- if not $valueFound -}} +true +{{- end -}} +{{- end -}} + + + +{{- /* Defines the name of the service account */ -}} +{{- define "newrelic.common.serviceAccount.name" -}} +{{- $localServiceAccount := "" -}} +{{- if get .Values "serviceAccount" | kindIs "map" -}} + {{- if (get .Values.serviceAccount "name" | kindIs "string") -}} + {{- $localServiceAccount = .Values.serviceAccount.name -}} + {{- end -}} +{{- end -}} + +{{- $globalServiceAccount := "" -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "serviceAccount" | kindIs "map" -}} + {{- if get $global.serviceAccount "name" | kindIs "string" -}} + {{- $globalServiceAccount = $global.serviceAccount.name -}} + {{- end -}} +{{- end -}} + +{{- if (include "newrelic.common.serviceAccount.create" .) -}} + {{- $localServiceAccount | default $globalServiceAccount | default (include "newrelic.common.naming.fullname" .) -}} +{{- else -}} + {{- $localServiceAccount | default $globalServiceAccount | default "default" -}} +{{- end -}} +{{- end -}} + + + +{{- /* Merge the global and local annotations for the service account */ -}} +{{- define "newrelic.common.serviceAccount.annotations" -}} +{{- $localServiceAccount := dict -}} +{{- if get .Values "serviceAccount" | kindIs "map" -}} + {{- if get .Values.serviceAccount "annotations" -}} + {{- $localServiceAccount = .Values.serviceAccount.annotations -}} + {{- end -}} +{{- end -}} + +{{- $globalServiceAccount := dict -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "serviceAccount" | kindIs "map" -}} + {{- if get $global.serviceAccount "annotations" -}} + {{- $globalServiceAccount = $global.serviceAccount.annotations -}} + {{- end -}} +{{- end -}} + +{{- $merged := mustMergeOverwrite $globalServiceAccount $localServiceAccount -}} + +{{- if $merged -}} + {{- toYaml $merged -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_staging.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_staging.tpl new file mode 100644 index 0000000000..bd9ad09bb9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_staging.tpl @@ -0,0 +1,39 @@ +{{- /* +Abstraction of the nrStaging toggle. +This helper allows to override the global `.global.nrStaging` with the value of `.nrStaging`. +Returns "true" if `nrStaging` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.nrStaging" -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if (get .Values "nrStaging" | kindIs "bool") -}} + {{- if .Values.nrStaging -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.nrStaging" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.nrStaging -}} + {{- end -}} +{{- else -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "nrStaging" | kindIs "bool" -}} + {{- if $global.nrStaging -}} + {{- $global.nrStaging -}} + {{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} + + + +{{- /* +Returns "true" of "false" directly instead of empty string (Helm falsiness) based on the exit of "newrelic.common.nrStaging" +*/ -}} +{{- define "newrelic.common.nrStaging.value" -}} +{{- if include "newrelic.common.nrStaging" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_tolerations.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_tolerations.tpl new file mode 100644 index 0000000000..e016b38e27 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_tolerations.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod tolerations */ -}} +{{- define "newrelic.common.tolerations" -}} + {{- if .Values.tolerations -}} + {{- toYaml .Values.tolerations -}} + {{- else if .Values.global -}} + {{- if .Values.global.tolerations -}} + {{- toYaml .Values.global.tolerations -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_verbose-log.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_verbose-log.tpl new file mode 100644 index 0000000000..2286d46815 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/templates/_verbose-log.tpl @@ -0,0 +1,54 @@ +{{- /* +Abstraction of the verbose toggle. +This helper allows to override the global `.global.verboseLog` with the value of `.verboseLog`. +Returns "true" if `verbose` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.verboseLog" -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if (get .Values "verboseLog" | kindIs "bool") -}} + {{- if .Values.verboseLog -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.verboseLog" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.verboseLog -}} + {{- end -}} +{{- else -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "verboseLog" | kindIs "bool" -}} + {{- if $global.verboseLog -}} + {{- $global.verboseLog -}} + {{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} + + + +{{- /* +Abstraction of the verbose toggle. +This helper abstracts the function "newrelic.common.verboseLog" to return true or false directly. +*/ -}} +{{- define "newrelic.common.verboseLog.valueAsBoolean" -}} +{{- if include "newrelic.common.verboseLog" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} + + + +{{- /* +Abstraction of the verbose toggle. +This helper abstracts the function "newrelic.common.verboseLog" to return 1 or 0 directly. +*/ -}} +{{- define "newrelic.common.verboseLog.valueAsInt" -}} +{{- if include "newrelic.common.verboseLog" . -}} +1 +{{- else -}} +0 +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/values.yaml new file mode 100644 index 0000000000..75e2d112ad --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/charts/common-library/values.yaml @@ -0,0 +1 @@ +# values are not needed for the library chart, however this file is still needed for helm lint to work. diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/ci/test-values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/ci/test-values.yaml new file mode 100644 index 0000000000..ac5ed6bb0c --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/ci/test-values.yaml @@ -0,0 +1,6 @@ +licenseKey: fakeLicenseKey +cluster: test-cluster-name +images: + configurator: + repository: ct/prometheus-configurator + tag: ct diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/static/lowdatamodedefaults.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/static/lowdatamodedefaults.yaml new file mode 100644 index 0000000000..726815755d --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/static/lowdatamodedefaults.yaml @@ -0,0 +1,6 @@ +# This file contains an entry of the array `extra_write_relabel_configs` to filter +# metrics on Low Data Mode. These metrics are already collected by the New Relic Kubernetes Integration. +low_data_mode: +- action: drop + source_labels: [__name__] + regex: "kube_.+|container_.+|machine_.+|cadvisor_.+" diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/static/metrictyperelabeldefaults.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/static/metrictyperelabeldefaults.yaml new file mode 100644 index 0000000000..c0a2774094 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/static/metrictyperelabeldefaults.yaml @@ -0,0 +1,17 @@ +# This file contains an entry of the array `extra_write_relabel_configs` to override metric types. +# https://docs.newrelic.com/docs/infrastructure/prometheus-integrations/install-configure-remote-write/set-your-prometheus-remote-write-integration#override-mapping +metrics_type_relabel: +- source_labels: [__name__] + separator: ; + regex: timeseries_write_(.*) # Cockroach + target_label: newrelic_metric_type + replacement: counter + action: replace +- source_labels: [__name__] + separator: ; + regex: sql_byte(.*) # Cockroach + target_label: newrelic_metric_type + replacement: counter + action: replace +# Note that adding more elements to this list could cause a possible breaking change to users already leveraging affected metrics. +# Therefore, before adding new entries check if any users is relying already on those metrics and warn them. diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/templates/_helpers.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/templates/_helpers.tpl new file mode 100644 index 0000000000..6cc58e251d --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/templates/_helpers.tpl @@ -0,0 +1,165 @@ +{{- /* Return the newrelic-prometheus configuration */ -}} + +{{- /* it builds the common configuration from configurator config, cluster name and custom attributes */ -}} +{{- define "newrelic-prometheus.configurator.common" -}} +{{- $tmp := dict "external_labels" (dict "cluster_name" (include "newrelic.common.cluster" . )) -}} + +{{- if .Values.config -}} + {{- if .Values.config.common -}} + {{- $_ := mustMerge $tmp .Values.config.common -}} + {{- end -}} +{{- end -}} + +{{- $tmpCustomAttribute := dict "external_labels" (include "newrelic.common.customAttributes" . | fromYaml ) -}} +{{- $tmp = mustMerge $tmp $tmpCustomAttribute -}} + +common: +{{- $tmp | toYaml | nindent 2 -}} + +{{- end -}} + + +{{- /* it builds the newrelic_remote_write configuration from configurator config */ -}} +{{- define "newrelic-prometheus.configurator.newrelic_remote_write" -}} +{{- $tmp := dict -}} + +{{- if include "newrelic.common.nrStaging" . -}} + {{- $_ := set $tmp "staging" true -}} +{{- end -}} + +{{- if include "newrelic.common.fedramp.enabled" . -}} + {{- $_ := set $tmp "fedramp" (dict "enabled" true) -}} +{{- end -}} + +{{- $extra_write_relabel_configs :=(include "newrelic-prometheus.configurator.extra_write_relabel_configs" . | fromYaml) -}} +{{- if ne (len $extra_write_relabel_configs.list) 0 -}} + {{- $_ := set $tmp "extra_write_relabel_configs" $extra_write_relabel_configs.list -}} +{{- end -}} + +{{- if .Values.config -}} +{{- if .Values.config.newrelic_remote_write -}} + {{- $tmp = mustMerge $tmp .Values.config.newrelic_remote_write -}} +{{- end -}} +{{- end -}} + +{{- if not (empty $tmp) -}} + {{- dict "newrelic_remote_write" $tmp | toYaml -}} +{{- end -}} + +{{- end -}} + +{{- /* it builds the extra_write_relabel_configs configuration merging: lowdatamode, user ones, and metrictyperelabeldefaults */ -}} +{{- define "newrelic-prometheus.configurator.extra_write_relabel_configs" -}} + +{{- $extra_write_relabel_configs := list -}} +{{- if (include "newrelic.common.lowDataMode" .) -}} + {{- $lowDataModeRelabelConfig := .Files.Get "static/lowdatamodedefaults.yaml" | fromYaml -}} + {{- $extra_write_relabel_configs = concat $extra_write_relabel_configs $lowDataModeRelabelConfig.low_data_mode -}} +{{- end -}} + +{{- if .Values.metric_type_override -}} + {{- if .Values.metric_type_override.enabled -}} + {{- $metricTypeOverride := .Files.Get "static/metrictyperelabeldefaults.yaml" | fromYaml -}} + {{- $extra_write_relabel_configs = concat $extra_write_relabel_configs $metricTypeOverride.metrics_type_relabel -}} + {{- end -}} +{{- end -}} + +{{- if .Values.config -}} +{{- if .Values.config.newrelic_remote_write -}} + {{- /* it concatenates the defined 'extra_write_relabel_configs' to the ones defined in lowDataMode */ -}} + {{- if .Values.config.newrelic_remote_write.extra_write_relabel_configs -}} + {{- $extra_write_relabel_configs = concat $extra_write_relabel_configs .Values.config.newrelic_remote_write.extra_write_relabel_configs -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{- /* sadly in helm we cannot pass back a list without putting it into a tmp dict */ -}} +{{ dict "list" $extra_write_relabel_configs | toYaml}} + +{{- end -}} + + +{{- /* it builds the extra_remote_write configuration from configurator config */ -}} +{{- define "newrelic-prometheus.configurator.extra_remote_write" -}} +{{- if .Values.config -}} + {{- if .Values.config.extra_remote_write -}} +extra_remote_write: + {{- .Values.config.extra_remote_write | toYaml | nindent 2 -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{- define "newrelic-prometheus.configurator.static_targets" -}} +{{- if .Values.config -}} + {{- if .Values.config.static_targets -}} +static_targets: + {{- .Values.config.static_targets | toYaml | nindent 2 -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{- define "newrelic-prometheus.configurator.extra_scrape_configs" -}} +{{- if .Values.config -}} + {{- if .Values.config.extra_scrape_configs -}} +extra_scrape_configs: + {{- .Values.config.extra_scrape_configs | toYaml | nindent 2 -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{- define "newrelic-prometheus.configurator.kubernetes" -}} +{{- if .Values.config -}} +{{- if .Values.config.kubernetes -}} +kubernetes: + {{- if .Values.config.kubernetes.jobs }} + jobs: + {{- .Values.config.kubernetes.jobs | toYaml | nindent 2 -}} + {{- end -}} + + {{- if .Values.config.kubernetes.integrations_filter }} + integrations_filter: + {{- .Values.config.kubernetes.integrations_filter | toYaml | nindent 4 -}} + {{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{- define "newrelic-prometheus.configurator.sharding" -}} + {{- if .Values.sharding -}} +sharding: + total_shards_count: {{ include "newrelic-prometheus.configurator.replicas" . }} + {{- end -}} +{{- end -}} + +{{- define "newrelic-prometheus.configurator.replicas" -}} + {{- if .Values.sharding -}} +{{- .Values.sharding.total_shards_count | default 1 }} + {{- else -}} +1 + {{- end -}} +{{- end -}} + +{{- /* +Return the proper configurator image name +{{ include "newrelic-prometheus.configurator.images.configurator_image" ( dict "imageRoot" .Values.path.to.the.image "context" .) }} +*/ -}} +{{- define "newrelic-prometheus.configurator.configurator_image" -}} + {{- $registryName := include "newrelic.common.images.registry" ( dict "imageRoot" .imageRoot "context" .context) -}} + {{- $repositoryName := include "newrelic.common.images.repository" .imageRoot -}} + {{- $tag := include "newrelic-prometheus.configurator.configurator_image.tag" ( dict "imageRoot" .imageRoot "context" .context) -}} + + {{- if $registryName -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag | quote -}} + {{- else -}} + {{- printf "%s:%s" $repositoryName $tag | quote -}} + {{- end -}} +{{- end -}} + + +{{- /* +Return the proper image tag for the configurator image +{{ include "newrelic-prometheus.configurator.configurator_image.tag" ( dict "imageRoot" .Values.path.to.the.image "context" .) }} +*/ -}} +{{- define "newrelic-prometheus.configurator.configurator_image.tag" -}} + {{- .imageRoot.tag | default .context.Chart.Annotations.configuratorVersion | toString -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/templates/clusterrole.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/templates/clusterrole.yaml new file mode 100644 index 0000000000..e9d4208e2f --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/templates/clusterrole.yaml @@ -0,0 +1,24 @@ +{{- if .Values.rbac.create }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "newrelic.common.naming.fullname" . }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +rules: + - apiGroups: + - "" + resources: + - endpoints + - services + - pods + - services + verbs: + - get + - list + - watch + - nonResourceURLs: + - "/metrics" + verbs: + - get +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/templates/clusterrolebinding.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/templates/clusterrolebinding.yaml new file mode 100644 index 0000000000..44244653ff --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/templates/clusterrolebinding.yaml @@ -0,0 +1,16 @@ +{{- if .Values.rbac.create }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "newrelic.common.naming.fullname" . }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "newrelic.common.naming.fullname" . }} +subjects: +- kind: ServiceAccount + name: {{ include "newrelic.common.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/templates/configmap.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/templates/configmap.yaml new file mode 100644 index 0000000000..b775aca746 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/templates/configmap.yaml @@ -0,0 +1,31 @@ +kind: ConfigMap +metadata: + name: {{ include "newrelic.common.naming.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +apiVersion: v1 +data: + config.yaml: |- + # Configuration for newrelic-prometheus-configurator + {{- with (include "newrelic-prometheus.configurator.newrelic_remote_write" . ) -}} + {{- . | nindent 4 }} + {{- end -}} + {{- with (include "newrelic-prometheus.configurator.extra_remote_write" . ) -}} + {{- . | nindent 4 }} + {{- end -}} + {{- with (include "newrelic-prometheus.configurator.static_targets" . ) -}} + {{- . | nindent 4 }} + {{- end -}} + {{- with (include "newrelic-prometheus.configurator.extra_scrape_configs" . ) -}} + {{- . | nindent 4 }} + {{- end -}} + {{- with (include "newrelic-prometheus.configurator.common" . ) -}} + {{- . | nindent 4 }} + {{- end -}} + {{- with (include "newrelic-prometheus.configurator.kubernetes" . ) -}} + {{- . | nindent 4 }} + {{- end -}} + {{- with (include "newrelic-prometheus.configurator.sharding" . ) -}} + {{- . | nindent 4 }} + {{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/templates/secret.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/templates/secret.yaml new file mode 100644 index 0000000000..f558ee86c9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/templates/secret.yaml @@ -0,0 +1,2 @@ +{{- /* Common library will take care of creating the secret or not. */}} +{{- include "newrelic.common.license.secret" . }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/templates/serviceaccount.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/templates/serviceaccount.yaml new file mode 100644 index 0000000000..b1e74523e5 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if include "newrelic.common.serviceAccount.create" . -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + {{- if include "newrelic.common.serviceAccount.annotations" . }} + annotations: + {{- include "newrelic.common.serviceAccount.annotations" . | nindent 4 }} + {{- end }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "newrelic.common.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/templates/statefulset.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/templates/statefulset.yaml new file mode 100644 index 0000000000..846c41c233 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/templates/statefulset.yaml @@ -0,0 +1,157 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "newrelic.common.naming.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + serviceName: {{ include "newrelic.common.naming.fullname" . }}-headless + selector: + matchLabels: + {{- include "newrelic.common.labels.selectorLabels" . | nindent 6 }} + replicas: {{ include "newrelic-prometheus.configurator.replicas" . }} + template: + metadata: + annotations: + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "newrelic.common.labels.podLabels" . | nindent 8 }} + spec: + {{- with include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" (list .Values.images.pullSecrets) "context" .) }} + imagePullSecrets: + {{- . | nindent 8 }} + {{- end }} + + {{- with include "newrelic.common.priorityClassName" . }} + priorityClassName: {{ . }} + {{- end }} + {{- with include "newrelic.common.securityContext.pod" . }} + securityContext: + {{- . | nindent 8 }} + {{- end }} + + {{- with include "newrelic.common.dnsConfig" . }} + dnsConfig: + {{- . | nindent 8 }} + {{- end }} + + hostNetwork: {{ include "newrelic.common.hostNetwork.value" . }} + {{- if include "newrelic.common.hostNetwork" . }} + dnsPolicy: ClusterFirstWithHostNet + {{- end }} + + serviceAccountName: {{ include "newrelic.common.serviceAccount.name" . }} + + initContainers: + - name: configurator + {{- with include "newrelic.common.securityContext.container" . }} + securityContext: + {{- . | nindent 12 }} + {{- end }} + image: {{ include "newrelic-prometheus.configurator.configurator_image" ( dict "imageRoot" .Values.images.configurator "context" .) }} + imagePullPolicy: {{ .Values.images.configurator.pullPolicy }} + args: + - --input=/etc/configurator/config.yaml + - --output=/etc/prometheus/config/config.yaml + {{- if include "newrelic.common.verboseLog" . }} + - --verbose=true + {{- end }} + {{- with .Values.resources.configurator }} + resources: {{ toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + - name: configurator-config + mountPath: /etc/configurator/ + - name: prometheus-config + mountPath: /etc/prometheus/config + env: + - name: NR_PROM_DATA_SOURCE_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NR_PROM_LICENSE_KEY + valueFrom: + secretKeyRef: + name: {{ include "newrelic.common.license.secretName" . }} + key: {{ include "newrelic.common.license.secretKeyName" . }} + - name: NR_PROM_CHART_VERSION + value: {{ .Chart.Version }} + + containers: + - name: prometheus + {{- with include "newrelic.common.securityContext.container" . }} + securityContext: + {{- . | nindent 12 }} + {{- end }} + image: {{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.images.prometheus "context" .) }} + imagePullPolicy: {{ .Values.images.prometheus.pullPolicy }} + ports: + - containerPort: 9090 + protocol: TCP + livenessProbe: + httpGet: + path: /-/healthy + port: 9090 + scheme: HTTP + initialDelaySeconds: 10 + periodSeconds: 15 + timeoutSeconds: 10 + failureThreshold: 3 + successThreshold: 1 + readinessProbe: + httpGet: + path: /-/ready + port: 9090 + scheme: HTTP + initialDelaySeconds: 10 + periodSeconds: 5 + timeoutSeconds: 4 + failureThreshold: 3 + successThreshold: 1 + args: + - --config.file=/etc/prometheus/config/config.yaml + - --enable-feature=agent,expand-external-labels + - --storage.agent.retention.max-time=30m + - --storage.agent.wal-truncate-frequency=30m + - --storage.agent.path=/etc/prometheus/storage + {{- if include "newrelic.common.verboseLog" . }} + - --log.level=debug + {{- end }} + {{- with .Values.resources.prometheus }} + resources: {{ toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + - name: prometheus-config + mountPath: /etc/prometheus/config + - name: prometheus-storage + mountPath: /etc/prometheus/storage + {{- with .Values.extraVolumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} + + volumes: + - name: configurator-config + configMap: + name: {{ include "newrelic.common.naming.fullname" . }} + - name: prometheus-config + emptyDir: {} + - name: prometheus-storage + emptyDir: {} + {{- with .Values.extraVolumes }} + {{- toYaml . | nindent 8 }} + {{- end }} + nodeSelector: + kubernetes.io/os: linux + {{ include "newrelic.common.nodeSelector" . | nindent 8 }} + {{- with include "newrelic.common.affinity" . }} + affinity: + {{- . | nindent 8 }} + {{- end }} + {{- with include "newrelic.common.tolerations" . }} + tolerations: + {{- . | nindent 8 }} + {{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/tests/configmap_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/tests/configmap_test.yaml new file mode 100644 index 0000000000..f2dd0468ea --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/tests/configmap_test.yaml @@ -0,0 +1,572 @@ +suite: test configmap +templates: + - templates/configmap.yaml +tests: + - it: config with defaults + set: + licenseKey: license-key-test + cluster: cluster-test + asserts: + - equal: + path: data["config.yaml"] + value: |- + # Configuration for newrelic-prometheus-configurator + newrelic_remote_write: + extra_write_relabel_configs: + - action: replace + regex: timeseries_write_(.*) + replacement: counter + separator: ; + source_labels: + - __name__ + target_label: newrelic_metric_type + - action: replace + regex: sql_byte(.*) + replacement: counter + separator: ; + source_labels: + - __name__ + target_label: newrelic_metric_type + static_targets: + jobs: + - extra_metric_relabel_config: + - action: keep + regex: prometheus_agent_active_series|prometheus_target_interval_length_seconds|prometheus_target_scrape_pool_targets|prometheus_remote_storage_samples_pending|prometheus_remote_storage_samples_in_total|prometheus_remote_storage_samples_retried_total|prometheus_agent_corruptions_total|prometheus_remote_storage_shards|prometheus_sd_kubernetes_events_total|prometheus_agent_checkpoint_creations_failed_total|prometheus_agent_checkpoint_deletions_failed_total|prometheus_remote_storage_samples_dropped_total|prometheus_remote_storage_samples_failed_total|prometheus_sd_kubernetes_http_request_total|prometheus_agent_truncate_duration_seconds_sum|prometheus_build_info|process_resident_memory_bytes|process_virtual_memory_bytes|process_cpu_seconds_total|prometheus_remote_storage_bytes_total + source_labels: + - __name__ + job_name: self-metrics + skip_sharding: true + targets: + - localhost:9090 + common: + external_labels: + cluster_name: cluster-test + scrape_interval: 30s + kubernetes: + jobs: + - job_name_prefix: default + target_discovery: + endpoints: true + filter: + annotations: + prometheus.io/scrape: true + pod: true + - integrations_filter: + enabled: false + job_name_prefix: newrelic + target_discovery: + endpoints: true + filter: + annotations: + newrelic.io/scrape: true + pod: true + integrations_filter: + app_values: + - redis + - traefik + - calico + - nginx + - coredns + - kube-dns + - etcd + - cockroachdb + - velero + - harbor + - argocd + enabled: true + source_labels: + - app.kubernetes.io/name + - app.newrelic.io/name + - k8s-app + + - it: staging is enabled + set: + licenseKey: license-key-test + cluster: cluster-test + nrStaging: true + metric_type_override: + enabled: false + config: + static_targets: # Set empty to make this test simple + asserts: + - matchRegex: + path: data["config.yaml"] + pattern: "newrelic_remote_write:\n staging: true" # We do not want to test the whole YAML + + - it: fedramp is enabled + set: + licenseKey: license-key-test + cluster: cluster-test + fedramp: + enabled: true + metric_type_override: + enabled: false + config: + static_targets: # Set empty to make this test simple + asserts: + - matchRegex: + path: data["config.yaml"] + pattern: "newrelic_remote_write:\n fedramp:\n enabled: true" # We do not want to test the whole YAML + + - it: config including remote_write most possible sections + set: + licenseKey: license-key-test + cluster: cluster-test + nrStaging: true + config: + newrelic_remote_write: + proxy_url: http://proxy.url + remote_timeout: 30s + tls_config: + insecure_skip_verify: true + queue_config: + retry_on_http_429: false + extra_write_relabel_configs: + - source_labels: + - __name__ + - instance + regex: node_memory_active_bytes;localhost:9100 + action: drop + extra_remote_write: + - url: "https://second.remote.write" + # Set empty to make this test simple + static_targets: + kubernetes: + asserts: + - equal: + path: data["config.yaml"] + value: |- + # Configuration for newrelic-prometheus-configurator + newrelic_remote_write: + extra_write_relabel_configs: + - action: replace + regex: timeseries_write_(.*) + replacement: counter + separator: ; + source_labels: + - __name__ + target_label: newrelic_metric_type + - action: replace + regex: sql_byte(.*) + replacement: counter + separator: ; + source_labels: + - __name__ + target_label: newrelic_metric_type + - action: drop + regex: node_memory_active_bytes;localhost:9100 + source_labels: + - __name__ + - instance + proxy_url: http://proxy.url + queue_config: + retry_on_http_429: false + remote_timeout: 30s + staging: true + tls_config: + insecure_skip_verify: true + extra_remote_write: + - url: https://second.remote.write + common: + external_labels: + cluster_name: cluster-test + scrape_interval: 30s + + - it: config including remote_write.extra_write_relabel_configs and not metric relabels + set: + licenseKey: license-key-test + cluster: cluster-test + metric_type_override: + enabled: false + config: + newrelic_remote_write: + extra_write_relabel_configs: + - source_labels: + - __name__ + - instance + regex: node_memory_active_bytes;localhost:9100 + action: drop + + static_targets: + kubernetes: + asserts: + - equal: + path: data["config.yaml"] + value: |- + # Configuration for newrelic-prometheus-configurator + newrelic_remote_write: + extra_write_relabel_configs: + - action: drop + regex: node_memory_active_bytes;localhost:9100 + source_labels: + - __name__ + - instance + common: + external_labels: + cluster_name: cluster-test + scrape_interval: 30s + + - it: cluster_name is set from global + set: + licenseKey: license-key-test + global: + cluster: "test" + metric_type_override: + enabled: false + config: + # Set empty to make this test simple + static_targets: + kubernetes: + asserts: + - equal: + path: data["config.yaml"] + value: |- + # Configuration for newrelic-prometheus-configurator + common: + external_labels: + cluster_name: test + scrape_interval: 30s + - it: cluster_name local value has precedence over global precedence + set: + licenseKey: license-key-test + global: + cluster: "test" + cluster: "test2" + metric_type_override: + enabled: false + config: + # Set empty to make this test simple + static_targets: + kubernetes: + asserts: + - equal: + path: data["config.yaml"] + value: |- + # Configuration for newrelic-prometheus-configurator + common: + external_labels: + cluster_name: test2 + scrape_interval: 30s + - it: cluster_name is not overwritten from customAttributes + set: + licenseKey: license-key-test + global: + cluster: "test" + cluster: "test2" + customAttributes: + cluster_name: "test3" + metric_type_override: + enabled: false + config: + # Set empty to make this test simple + static_targets: + kubernetes: + asserts: + - equal: + path: data["config.yaml"] + value: |- + # Configuration for newrelic-prometheus-configurator + common: + external_labels: + cluster_name: test2 + scrape_interval: 30s + + - it: cluster_name has precedence over extra labels has precedence over customAttributes + set: + licenseKey: license-key-test + cluster: test + customAttributes: + attribute: "value" + one: error + cluster_name: "different" + metric_type_override: + enabled: false + config: + common: + external_labels: + one: two + cluster_name: "different" + scrape_interval: 15 + # Set empty to make this test simple + static_targets: + kubernetes: + asserts: + - equal: + path: data["config.yaml"] + value: |- + # Configuration for newrelic-prometheus-configurator + common: + external_labels: + attribute: value + cluster_name: test + one: two + scrape_interval: 15 + + - it: config including static_targets overwritten with most possible sections + set: + licenseKey: license-key-test + cluster: cluster-test + metric_type_override: + enabled: false + config: + static_targets: + jobs: + - job_name: my-custom-target-authorization-full + targets: + - "192.168.3.1:2379" + params: + q: [ "puppies" ] + oe: [ "utf8" ] + scheme: "https" + body_size_limit: 100MiB + sample_limit: 2000 + target_limit: 2000 + label_limit: 2000 + label_name_length_limit: 2000 + label_value_length_limit: 2000 + scrape_interval: 15s + scrape_timeout: 15s + tls_config: + insecure_skip_verify: true + ca_file: /path/to/ca.crt + key_file: /path/to/key.crt + cert_file: /path/to/cert.crt + server_name: server.name + min_version: TLS12 + authorization: + type: Bearer + credentials: "fancy-credentials" + extra_relabel_config: + - source_labels: [ '__name__', 'instance' ] + regex: node_memory_active_bytes;localhost:9100 + action: drop + extra_metric_relabel_config: + - source_labels: [ '__name__', 'instance' ] + regex: node_memory_active_bytes;localhost:9100 + action: drop + extra_scrape_configs: + - job_name: extra-scrape-config + static_configs: + - targets: + - "192.168.3.1:2379" + labels: + label1: value1 + label2: value2 + scrape_interval: 15s + scrape_timeout: 15s + tls_config: + insecure_skip_verify: true + ca_file: /path/to/ca.crt + key_file: /path/to/key.crt + cert_file: /path/to/cert.crt + server_name: server.name + min_version: TLS12 + authorization: + type: Bearer + credentials: "fancy-credentials" + relabel_configs: + - source_labels: [ '__name__', 'instance' ] + regex: node_memory_active_bytes;localhost:9100 + action: drop + metric_relabel_configs: + - source_labels: [ '__name__', 'instance' ] + regex: node_memory_active_bytes;localhost:9100 + action: drop + # Set empty to make this test simple + kubernetes: + asserts: + - equal: + path: data["config.yaml"] + value: |- + # Configuration for newrelic-prometheus-configurator + static_targets: + jobs: + - authorization: + credentials: fancy-credentials + type: Bearer + body_size_limit: 100MiB + extra_metric_relabel_config: + - action: drop + regex: node_memory_active_bytes;localhost:9100 + source_labels: + - __name__ + - instance + extra_relabel_config: + - action: drop + regex: node_memory_active_bytes;localhost:9100 + source_labels: + - __name__ + - instance + job_name: my-custom-target-authorization-full + label_limit: 2000 + label_name_length_limit: 2000 + label_value_length_limit: 2000 + params: + oe: + - utf8 + q: + - puppies + sample_limit: 2000 + scheme: https + scrape_interval: 15s + scrape_timeout: 15s + target_limit: 2000 + targets: + - 192.168.3.1:2379 + tls_config: + ca_file: /path/to/ca.crt + cert_file: /path/to/cert.crt + insecure_skip_verify: true + key_file: /path/to/key.crt + min_version: TLS12 + server_name: server.name + extra_scrape_configs: + - authorization: + credentials: fancy-credentials + type: Bearer + job_name: extra-scrape-config + metric_relabel_configs: + - action: drop + regex: node_memory_active_bytes;localhost:9100 + source_labels: + - __name__ + - instance + relabel_configs: + - action: drop + regex: node_memory_active_bytes;localhost:9100 + source_labels: + - __name__ + - instance + scrape_interval: 15s + scrape_timeout: 15s + static_configs: + - labels: + label1: value1 + label2: value2 + targets: + - 192.168.3.1:2379 + tls_config: + ca_file: /path/to/ca.crt + cert_file: /path/to/cert.crt + insecure_skip_verify: true + key_file: /path/to/key.crt + min_version: TLS12 + server_name: server.name + common: + external_labels: + cluster_name: cluster-test + scrape_interval: 30s + + - it: kubernetes config section custom values + set: + licenseKey: license-key-test + cluster: cluster-test + metric_type_override: + enabled: false + config: + kubernetes: + integrations_filter: + enabled: false + jobs: + - job_name_prefix: pod-job + target_discovery: + pod: true + endpoints: false + filter: + annotations: + custom/scrape-pod: true + - job_name_prefix: endpoints-job + target_discovery: + pod: false + endpoints: true + filter: + annotations: + custom/scrape-endpoints: true + # Set empty to make this test simple + static_targets: + asserts: + - equal: + path: data["config.yaml"] + value: |- + # Configuration for newrelic-prometheus-configurator + common: + external_labels: + cluster_name: cluster-test + scrape_interval: 30s + kubernetes: + jobs: + - job_name_prefix: pod-job + target_discovery: + endpoints: false + filter: + annotations: + custom/scrape-pod: true + pod: true + - job_name_prefix: endpoints-job + target_discovery: + endpoints: true + filter: + annotations: + custom/scrape-endpoints: true + pod: false + integrations_filter: + app_values: + - redis + - traefik + - calico + - nginx + - coredns + - kube-dns + - etcd + - cockroachdb + - velero + - harbor + - argocd + enabled: false + source_labels: + - app.kubernetes.io/name + - app.newrelic.io/name + - k8s-app + + - it: sharding empty not propagated + set: + licenseKey: license-key-test + cluster: cluster-test + sharding: + metric_type_override: + enabled: false + config: + kubernetes: + static_targets: + asserts: + - equal: + path: data["config.yaml"] + value: |- + # Configuration for newrelic-prometheus-configurator + common: + external_labels: + cluster_name: cluster-test + scrape_interval: 30s + + - it: sharding config custom values + set: + licenseKey: license-key-test + cluster: cluster-test + sharding: + total_shards_count: 2 + metric_type_override: + enabled: false + config: + kubernetes: + static_targets: + asserts: + - equal: + path: data["config.yaml"] + value: |- + # Configuration for newrelic-prometheus-configurator + common: + external_labels: + cluster_name: cluster-test + scrape_interval: 30s + sharding: + total_shards_count: 2 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/tests/configurator_image_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/tests/configurator_image_test.yaml new file mode 100644 index 0000000000..0f5da69bf8 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/tests/configurator_image_test.yaml @@ -0,0 +1,57 @@ +suite: test image +templates: + - templates/statefulset.yaml + - templates/configmap.yaml +tests: + - it: configurator image is set + set: + licenseKey: license-key-test + cluster: cluster-test + images: + configurator: + tag: "test" + pullPolicy: Never + prometheus: + tag: "test-2" + asserts: + - template: templates/statefulset.yaml + equal: + path: spec.template.spec.initContainers[0].image + value: "newrelic/newrelic-prometheus-configurator:test" + - equal: + path: spec.template.spec.initContainers[0].imagePullPolicy + value: "Never" + template: templates/statefulset.yaml + - template: templates/statefulset.yaml + equal: + path: spec.template.spec.containers[0].image + value: "quay.io/prometheus/prometheus:test-2" + - equal: + path: spec.template.spec.containers[0].imagePullPolicy + value: "IfNotPresent" + template: templates/statefulset.yaml + + - it: has a linux node selector by default + set: + licenseKey: license-key-test + cluster: my-cluster + asserts: + - equal: + path: spec.template.spec.nodeSelector + value: + kubernetes.io/os: linux + template: templates/statefulset.yaml + + - it: has a linux node selector and additional selectors + set: + licenseKey: license-key-test + cluster: my-cluster + nodeSelector: + aCoolTestLabel: aCoolTestValue + asserts: + - equal: + path: spec.template.spec.nodeSelector + value: + kubernetes.io/os: linux + aCoolTestLabel: aCoolTestValue + template: templates/statefulset.yaml diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/tests/integration_filters_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/tests/integration_filters_test.yaml new file mode 100644 index 0000000000..d1813f1352 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/tests/integration_filters_test.yaml @@ -0,0 +1,119 @@ +suite: test configmap with IntegrationFilter +templates: + - templates/configmap.yaml +tests: + - it: config with IntegrationFilter true + set: + licenseKey: license-key-test + cluster: cluster-test + metric_type_override: + enabled: false + config: + kubernetes: + integrations_filter: + enabled: true + # Set empty to make this test simple + static_targets: + asserts: + - equal: + path: data["config.yaml"] + value: |- + # Configuration for newrelic-prometheus-configurator + common: + external_labels: + cluster_name: cluster-test + scrape_interval: 30s + kubernetes: + jobs: + - job_name_prefix: default + target_discovery: + endpoints: true + filter: + annotations: + prometheus.io/scrape: true + pod: true + - integrations_filter: + enabled: false + job_name_prefix: newrelic + target_discovery: + endpoints: true + filter: + annotations: + newrelic.io/scrape: true + pod: true + integrations_filter: + app_values: + - redis + - traefik + - calico + - nginx + - coredns + - kube-dns + - etcd + - cockroachdb + - velero + - harbor + - argocd + enabled: true + source_labels: + - app.kubernetes.io/name + - app.newrelic.io/name + - k8s-app + + - it: config with IntegrationFilter false + set: + licenseKey: license-key-test + cluster: cluster-test + metric_type_override: + enabled: false + config: + kubernetes: + integrations_filter: + enabled: false + # Set empty to make this test simple + static_targets: + asserts: + - equal: + path: data["config.yaml"] + value: |- + # Configuration for newrelic-prometheus-configurator + common: + external_labels: + cluster_name: cluster-test + scrape_interval: 30s + kubernetes: + jobs: + - job_name_prefix: default + target_discovery: + endpoints: true + filter: + annotations: + prometheus.io/scrape: true + pod: true + - integrations_filter: + enabled: false + job_name_prefix: newrelic + target_discovery: + endpoints: true + filter: + annotations: + newrelic.io/scrape: true + pod: true + integrations_filter: + app_values: + - redis + - traefik + - calico + - nginx + - coredns + - kube-dns + - etcd + - cockroachdb + - velero + - harbor + - argocd + enabled: false + source_labels: + - app.kubernetes.io/name + - app.newrelic.io/name + - k8s-app diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/tests/lowdatamode_configmap_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/tests/lowdatamode_configmap_test.yaml new file mode 100644 index 0000000000..ac3953df6d --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/tests/lowdatamode_configmap_test.yaml @@ -0,0 +1,138 @@ +suite: test configmap with LowDataMode +templates: + - templates/configmap.yaml +tests: + - it: config with lowDataMode true + set: + licenseKey: license-key-test + cluster: cluster-test + lowDataMode: true + metric_type_override: + enabled: false + config: + # Set empty to make this test simple + static_targets: + kubernetes: + asserts: + - equal: + path: data["config.yaml"] + value: |- + # Configuration for newrelic-prometheus-configurator + newrelic_remote_write: + extra_write_relabel_configs: + - action: drop + regex: kube_.+|container_.+|machine_.+|cadvisor_.+ + source_labels: + - __name__ + common: + external_labels: + cluster_name: cluster-test + scrape_interval: 30s + + - it: config with lowDataMode and nrStaging true + set: + licenseKey: license-key-test + cluster: cluster-test + lowDataMode: true + nrStaging: true + metric_type_override: + enabled: false + config: + # Set empty to make this test simple + static_targets: + kubernetes: + asserts: + - equal: + path: data["config.yaml"] + value: |- + # Configuration for newrelic-prometheus-configurator + newrelic_remote_write: + extra_write_relabel_configs: + - action: drop + regex: kube_.+|container_.+|machine_.+|cadvisor_.+ + source_labels: + - __name__ + staging: true + common: + external_labels: + cluster_name: cluster-test + scrape_interval: 30s + + - it: config with lowDataMode true from global config + set: + global: + lowDataMode: true + licenseKey: license-key-test + cluster: cluster-test + metric_type_override: + enabled: false + config: + # Set empty to make this test simple + static_targets: + kubernetes: + asserts: + - equal: + path: data["config.yaml"] + value: |- + # Configuration for newrelic-prometheus-configurator + newrelic_remote_write: + extra_write_relabel_configs: + - action: drop + regex: kube_.+|container_.+|machine_.+|cadvisor_.+ + source_labels: + - __name__ + common: + external_labels: + cluster_name: cluster-test + scrape_interval: 30s + + - it: existing relabel configs are appended to low data mode and metric_type_override relabel configs. + set: + lowDataMode: true + licenseKey: license-key-test + cluster: cluster-test + metric_type_override: + enabled: true + config: + newrelic_remote_write: + extra_write_relabel_configs: + - action: drop + regex: my_custom_metric_relabel_config + source_labels: + - __name__ + # Set empty to make this test simple + static_targets: + kubernetes: + asserts: + - equal: + path: data["config.yaml"] + value: |- + # Configuration for newrelic-prometheus-configurator + newrelic_remote_write: + extra_write_relabel_configs: + - action: drop + regex: kube_.+|container_.+|machine_.+|cadvisor_.+ + source_labels: + - __name__ + - action: replace + regex: timeseries_write_(.*) + replacement: counter + separator: ; + source_labels: + - __name__ + target_label: newrelic_metric_type + - action: replace + regex: sql_byte(.*) + replacement: counter + separator: ; + source_labels: + - __name__ + target_label: newrelic_metric_type + - action: drop + regex: my_custom_metric_relabel_config + source_labels: + - __name__ + common: + external_labels: + cluster_name: cluster-test + scrape_interval: 30s diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/values.yaml new file mode 100644 index 0000000000..2fb3ed7bca --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/newrelic-prometheus-agent/values.yaml @@ -0,0 +1,473 @@ +# -- Override the name of the chart +nameOverride: "" +# -- Override the full name of the release +fullnameOverride: "" + +# -- Name of the Kubernetes cluster monitored. Can be configured also with `global.cluster`. +# Note it will be set as an external label in prometheus configuration, it will have precedence over `config.common.external_labels.cluster_name` +# and `customAttributes.cluster_name``. +cluster: "" +# -- This set this license key to use. Can be configured also with `global.licenseKey` +licenseKey: "" +# -- In case you don't want to have the license key in you values, this allows you to point to a user created secret to get the key from there. Can be configured also with `global.customSecretName` +customSecretName: "" +# -- In case you don't want to have the license key in you values, this allows you to point to which secret key is the license key located. Can be configured also with `global.customSecretLicenseKey` +customSecretLicenseKey: "" + +# -- Adds extra attributes to prometheus external labels. Can be configured also with `global.customAttributes`. Please note, values defined +# in `common.config.externar_labels` will have precedence over `customAttributes`. +customAttributes: {} + +# Images used by the chart for prometheus and New Relic configurator. +# @default See `values.yaml` +images: + # -- The secrets that are needed to pull images from a custom registry. + pullSecrets: [] + + # -- Image for New Relic configurator. + # @default -- See `values.yaml` + configurator: + registry: "" + repository: newrelic/newrelic-prometheus-configurator + pullPolicy: IfNotPresent + # @default It defaults to `annotation.configuratorVersion` in `Chart.yaml`. + tag: "" + # -- Image for prometheus which is executed in agent mode. + # @default -- See `values.yaml` + prometheus: + registry: "" + repository: quay.io/prometheus/prometheus + pullPolicy: IfNotPresent + # @default It defaults to `appVersion` in `Chart.yaml`. + tag: "" + +# -- Volumes to mount in the containers +extraVolumes: [] +# -- Defines where to mount volumes specified with `extraVolumes` +extraVolumeMounts: [] + +# -- Settings controlling ServiceAccount creation. +# @default -- See `values.yaml` +serviceAccount: + # -- Whether the chart should automatically create the ServiceAccount objects required to run. + create: true + annotations: {} + # If not set and create is true, a name is generated using the full name template + name: "" + +# -- Additional labels for chart objects. Can be configured also with `global.labels` +labels: {} +# -- Annotations to be added to all pods created by the integration. +podAnnotations: {} +# -- Additional labels for chart pods. Can be configured also with `global.podLabels` +podLabels: {} + +# -- Resource limits to be added to all pods created by the integration. +# @default -- `{}` +resources: + prometheus: {} + +# -- Sets pod's priorityClassName. Can be configured also with `global.priorityClassName` +priorityClassName: "" +# -- (bool) Sets pod's hostNetwork. Can be configured also with `global.hostNetwork` +# @default -- `false` +hostNetwork: +# -- Sets security context (at pod level). Can be configured also with `global.podSecurityContext` +podSecurityContext: {} +# -- Sets security context (at container level). Can be configured also with `global.containerSecurityContext` +containerSecurityContext: {} + +# -- Sets pod's dnsConfig. Can be configured also with `global.dnsConfig` +dnsConfig: {} + +# Settings controlling RBAC objects creation. +rbac: + # -- Whether the chart should automatically create the RBAC objects required to run. + create: true + # -- Whether the chart should create Pod Security Policy objects. + pspEnabled: false + +# -- Sets pod/node affinities set almost globally. (See [Affinities and tolerations](README.md#affinities-and-tolerations)) +affinity: {} +# -- Sets pod's node selector almost globally. (See [Affinities and tolerations](README.md#affinities-and-tolerations)) +nodeSelector: {} +# -- Sets pod's tolerations to node taints almost globally. (See [Affinities and tolerations](README.md#affinities-and-tolerations)) +tolerations: [] + +# -- (bool) Send the metrics to the staging backend. Requires a valid staging license key. Can be configured also with `global.nrStaging` +# @default -- `false` +nrStaging: + +# -- (bool) Reduces the number of metrics sent in order to reduce costs. It can be configured also with `global.lowDataMode`. +# Specifically, it makes Prometheus stop reporting some Kubernetes cluster-specific metrics, you can see details in `static/lowdatamodedefaults.yaml`. +# @default -- false +lowDataMode: + +# -- It holds the configuration for metric type override. If enabled, a series of metric relabel configs will be added to +# `config.newrelic_remote_write.extra_write_relabel_configs`, you can check the whole list in `static/metrictyperelabeldefaults.yaml` +metric_type_override: + enabled: true + +# -- Set up Prometheus replicas to allow horizontal scalability. +# @default -- See `values.yaml` +sharding: + # -- Sets the number of Prometheus instances running on sharding mode. + # @default -- `1` + # total_shards_count: + +# -- (bool) Sets the debug log to Prometheus and prometheus-configurator or all integrations if it is set globally. Can be configured also with `global.verboseLog` +# @default -- `false` +verboseLog: + +# -- It holds the New Relic Prometheus configuration. Here you can easily set up Prometheus to get set metrics, discover +# ponds and endpoints Kubernetes and send metrics to New Relic using remote-write. +# @default -- See `values.yaml` +config: + # -- Include global configuration for Prometheus agent. + # @default -- See `values.yaml` + common: + # -- The labels to add to any timeseries that this Prometheus instance scrapes. + # @default -- `{}` + # external_labels: + # label_key_example: foo-bar + # -- How frequently to scrape targets by default, unless a different value is specified on the job. + scrape_interval: 30s + # -- The default timeout when scraping targets. + # @default -- `10s` + # scrape_timeout: + + # -- (object) Newrelic remote-write configuration settings. + # @default -- See `values.yaml` + newrelic_remote_write: + # # -- Includes additional [relabel configs](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config) + # # for the New Relic remote write. + # # @default -- `[]` + # extra_write_relabel_configs: [] + + # # Enable the extra_write_relabel_configs below for backwards compatibility with legacy POMI labels. + # # This helpful when migrating from POMI to ensure that Prometheus metrics will contain both labels (e.g. cluster_name and clusterName). + # # For more migration info, please visit the [migration guide](https://docs.newrelic.com/docs/infrastructure/prometheus-integrations/install-configure-prometheus-agent/migration-guide/). + # - source_labels: [namespace] + # action: replace + # target_label: namespaceName + # - source_labels: [node] + # action: replace + # target_label: nodeName + # - source_labels: [pod] + # action: replace + # target_label: podName + # - source_labels: [service] + # action: replace + # target_label: serviceName + # - source_labels: [cluster_name] + # action: replace + # target_label: clusterName + # - source_labels: [job] + # action: replace + # target_label: scrapedTargetKind + # - source_labels: [instance] + # action: replace + # target_label: scrapedTargetInstance + + # -- Set up the proxy used to send metrics to New Relic. + # @default -- `""` + # proxy_url: + + # -- # Timeout for requests to the remote write endpoint. + # @default -- `30s` + # remote_timeout: + + # -- Fine-tune remote-write behavior: . + # queue_config: + # -- Remote Write shard capacity. + # @default -- `2500` + # capacity: + # -- Maximum number of shards. + # @default -- `200` + # max_shards: + # -- Minimum number of shards. + # @default -- `1` + # min_shards: + # -- Maximum number of samples per send. + # @default -- `500` + # max_samples_per_send: + # -- Maximum time a sample will wait in the buffer. + # @default -- `5s` + # batch_send_deadline: + # -- Initial retry delay. Gets doubled for every retry. + # @default -- `30ms` + # min_backoff: + # -- Maximum retry delay. + # @default -- `5s` + # max_backoff: + # -- Retry upon receiving a 429 status code from the remote-write storage. + # @default -- `false` + # retry_on_http_429: + + # -- (object) It includes additional remote-write configuration. Note this configuration is not parsed, so valid + # [prometheus remote_write configuration](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#remote_write) + # should be provided. + extra_remote_write: + + # -- It allows defining scrape jobs for Kubernetes in a simple way. + # @default -- See `values.yaml` + kubernetes: + # NewRelic provides a list of Dashboards, alerts and entities for several Services. The integrations_filter configuration + # allows to scrape only the targets having this experience out of the box. + # If integrations_filter is enabled, then the jobs scrape merely the targets having one of the specified labels matching + # one of the values of app_values. + # Under the hood, a relabel_configs with 'action=keep' are generated, consider it in case any custom extra_relabel_config is needed. + integrations_filter: + # -- enabling the integration filters, merely the targets having one of the specified labels matching + # one of the values of app_values are scraped. Each job configuration can override this default. + enabled: true + # -- source_labels used to fetch label values in the relabel config added by the integration filters configuration + source_labels: ["app.kubernetes.io/name", "app.newrelic.io/name", "k8s-app"] + # -- app_values used to create the regex used in the relabel config added by the integration filters configuration. + # Note that a single regex will be created from this list, example: '.*(?i)(app1|app2|app3).*' + app_values: ["redis", "traefik", "calico", "nginx", "coredns", "kube-dns", "etcd", "cockroachdb", "velero", "harbor", "argocd"] + + # Kubernetes jobs define [kubernetes_sd_configs](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config) + # to discover and scrape Kubernetes objects. Besides, a set of relabel_configs are included in order to include some Kubernetes metadata as + # Labels. For example, address, metrics_path, URL scheme, prometheus_io_parameters, namespace, pod name, service name and labels are taken + # to set the corresponding labels. + # Please note, the relabeling allows configuring the pod/endpoints scrape using the following annotations: + # - `prometheus.io/scheme`: If the metrics endpoint is secured then you will need to set this to `https` + # - `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 for service endpoints or to + # the default 9102 for pods. + # - `prometheus.io/param_`: To include additional parameters in the scrape URL. + jobs: + # 'default' scrapes all targets having 'prometheus.io/scrape: true'. + # Out of the box, since kubernetes.integrations_filter.enabled=true then only targets selected by the integration filters are considered. + - job_name_prefix: default + target_discovery: + pod: true + endpoints: true + filter: + annotations: + prometheus.io/scrape: true + # -- integrations_filter configuration for this specific job. It overrides kubernetes.integrations_filter configuration + # integrations_filter: + + # 'newrelic' scrapes all targets having 'newrelic.io/scrape: true'. + # This is useful to extend the targets scraped by the 'default' job allowlisting services leveraging `newrelic.io/scrape` annotation + - job_name_prefix: newrelic + integrations_filter: + enabled: false + target_discovery: + pod: true + endpoints: true + filter: + annotations: + newrelic.io/scrape: true + + # -- Set up the job name prefix. The final Prometheus `job` name will be composed of + the target discovery kind. ie: `default-pod` + # @default -- `""` + # - job_name_prefix: + + # -- The target discovery field allows customizing how Kubernetes discovery works. + # target_discovery: + + # -- Whether pods should be discovered. + # @default -- `false` + # pod: + + # -- Whether endpoints should be discovered. + # @default -- `false` + # endpoints: + + # -- Defines filtering criteria, it is possible to set labels and/or annotations. All filters will apply (defined + # filters are taken into account as an "AND operation"). + # @default -- `{}` + # filter: + # -- Map of annotations that the targets should have. If only the annotation name is defined, the filter only checks if exists. + # @default -- `{}` + # annotations: + + # -- Map of labels that the targets should have. If only the label name is defined, the filter only checks if exists. + # @default -- `{}` + # labels: + + # -- Advanced configs of the Kubernetes service discovery `kuberentes_sd_config` options, + # check [prometheus documentation](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config) for details. + # Notice that using `filter` is the recommended way to filter targets to avoid adding load to the API Server. + # additional_config: + # kubeconfig_file: "" + # namespaces: {} + # selectors: {} + # attach_metadata: {} + + + # -- The HTTP resource path on which to fetch metrics from targets. + # Use `prometheus.io/path` pod/service annotation to override this or modify it here. + # @default -- `/metrics` + # metrics_path: + + # -- Optional HTTP URL parameters. + # Use `prometheus.io/param_` pod/service annotation to include additional parameters in the scrape url or modify it here. + # @default -- `{}` + # params: + + # -- Configures the protocol scheme used for requests. + # Annotate the service/pod with `prometheus.io/scheme=https` if the secured port is used or modify it here. + # @default -- `http` + # scheme: + + # -- How frequently to scrape targets from this job. + # @default -- defined in `common.scrape_interval` + # scrape_interval: + + # -- Per-scrape timeout when scraping this job. + # @default -- defined in `common.scrape_timeout` + # scrape_timeout: + + # -- Configures the scrape request's TLS settings. + # @default -- `{}` + # tls_config: + # -- CA certificate file path to validate API server certificate with. + # @default -- `""` + # ca_file: + + # -- Certificate and key files path for client cert authentication to the server. + # @default -- `""` + # cert_file: + # key_file: + + # Disable validation of the server certificate. + # @default -- `false` + # insecure_skip_verify: + + # -- Sets the `Authorization` Bearer token header on every scrape request + # @default -- `{}` + # authorization: + # Sets the credentials to the credentials read from the configured file. + # @default -- `""` + # credentials_file: + + # -- Sets the `Authorization` header on every scrape request with the configured username and password. + # @default -- `{}` + # basic_auth: + # username: + # password_file: + + # -- List of relabeling configurations. Used if needed to add any special filter or label manipulation before the scrape takes place. + # @default -- `[]` + # extra_relabel_config: + + # -- List of metric relabel configurations. Used it to filter metrics and labels after scrape. + # @default -- `[]` + # extra_metric_relabel_config: + + + # -- It allows defining scrape jobs for targets with static URLs. + # @default -- See `values.yaml`. + static_targets: + # -- List of static target jobs. By default, it defines a job to get self-metrics. Please note, if you define `static_target.jobs` and would like to keep + # self-metrics you need to include a job like the one defined by default. + # @default -- See `values.yaml`. + jobs: + - job_name: self-metrics + skip_sharding: true # sharding is skipped to obtain self-metrics from all Prometheus servers. + targets: + - "localhost:9090" + extra_metric_relabel_config: + - source_labels: [__name__] + regex: "\ + prometheus_agent_active_series|\ + prometheus_target_interval_length_seconds|\ + prometheus_target_scrape_pool_targets|\ + prometheus_remote_storage_samples_pending|\ + prometheus_remote_storage_samples_in_total|\ + prometheus_remote_storage_samples_retried_total|\ + prometheus_agent_corruptions_total|\ + prometheus_remote_storage_shards|\ + prometheus_sd_kubernetes_events_total|\ + prometheus_agent_checkpoint_creations_failed_total|\ + prometheus_agent_checkpoint_deletions_failed_total|\ + prometheus_remote_storage_samples_dropped_total|\ + prometheus_remote_storage_samples_failed_total|\ + prometheus_sd_kubernetes_http_request_total|\ + prometheus_agent_truncate_duration_seconds_sum|\ + prometheus_build_info|\ + process_resident_memory_bytes|\ + process_virtual_memory_bytes|\ + process_cpu_seconds_total|\ + prometheus_remote_storage_bytes_total" + action: keep + + # -- The job name assigned to scraped metrics by default. + # @default -- `""`. + # - job_name: + # -- List of target URLs to be scraped by this job. + # @default -- `[]`. + # targets: + + # -- Labels assigned to all metrics scraped from the targets. + # @default -- `{}`. + # labels: + + # -- The HTTP resource path on which to fetch metrics from targets. + # @default -- `/metrics` + # metrics_path: + + # -- Optional HTTP URL parameters. + # @default -- `{}` + # params: + + # -- Configures the protocol scheme used for requests. + # @default -- `http` + # scheme: + + # -- How frequently to scrape targets from this job. + # @default -- defined in `common.scrape_interval` + # scrape_interval: + + # -- Per-scrape timeout when scraping this job. + # @default -- defined in `common.scrape_timeout` + # scrape_timeout: + + # -- Configures the scrape request's TLS settings. + # @default -- `{}` + # tls_config: + # -- CA certificate file path to validate API server certificate with. + # @default -- `""` + # ca_file: + + # -- Certificate and key files path for client cert authentication to the server. + # @default -- `""` + # cert_file: + # key_file: + + # Disable validation of the server certificate. + # @default -- `false` + # insecure_skip_verify: + + # -- Sets the `Authorization` Bearer token header on every scrape request + # @default -- `{}` + # authorization: + # Sets the credentials to the credentials read from the configured file. + # @default -- `""` + # credentials_file: + + # -- Sets the `Authorization` header on every scrape request with the configured username and password. + # @default -- `{}` + # basic_auth: + # username: + # password_file: + + # -- List of relabeling configurations. Used if needed to add any special filter or label manipulation before the scrape takes place. + # @default -- `[]` + # extra_relabel_config: + + # -- List of metric relabel configurations. Used it to filter metrics and labels after scrape. + # @default -- `[]` + # extra_metric_relabel_config: + + + # -- It is possible to include extra scrape configuration in [prometheus format](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config). + # Please note, it should be a valid Prometheus configuration which will not be parsed by the chart. + # WARNING extra_scrape_configs is a raw Prometheus config. Therefore, the metrics collected thanks to it will not have by default the metadata (pod_name, service_name, ...) added by the configurator for the static or kubernetes jobs. + # This configuration should be used as a workaround whenever kubernetes and static job do not cover a particular use-case. + # @default -- `[]` + extra_scrape_configs: [] diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/Chart.lock b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/Chart.lock new file mode 100644 index 0000000000..d524c92920 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: common-library + repository: https://helm-charts.newrelic.com + version: 1.3.0 +digest: sha256:2e1da613fd8a52706bde45af077779c5d69e9e1641bdf5c982eaf6d1ac67a443 +generated: "2024-08-30T23:46:01.668441447Z" diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/Chart.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/Chart.yaml new file mode 100644 index 0000000000..227ce9c0c9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/Chart.yaml @@ -0,0 +1,26 @@ +apiVersion: v2 +appVersion: 2.10.5 +dependencies: +- name: common-library + repository: https://helm-charts.newrelic.com + version: 1.3.0 +description: A Helm chart to deploy the New Relic Kube Events router +home: https://docs.newrelic.com/docs/integrations/kubernetes-integration/kubernetes-events/install-kubernetes-events-integration +icon: https://newrelic.com/themes/custom/curio/assets/mediakit/NR_logo_Horizontal.svg +keywords: +- infrastructure +- newrelic +- monitoring +maintainers: +- name: juanjjaramillo + url: https://github.com/juanjjaramillo +- name: csongnr + url: https://github.com/csongnr +- name: dbudziwojskiNR + url: https://github.com/dbudziwojskiNR +name: nri-kube-events +sources: +- https://github.com/newrelic/nri-kube-events/ +- https://github.com/newrelic/nri-kube-events/tree/main/charts/nri-kube-events +- https://github.com/newrelic/infrastructure-agent/ +version: 3.10.5 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/README.md b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/README.md new file mode 100644 index 0000000000..75dd4b11a8 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/README.md @@ -0,0 +1,79 @@ +# nri-kube-events + +![Version: 3.10.5](https://img.shields.io/badge/Version-3.10.5-informational?style=flat-square) ![AppVersion: 2.10.5](https://img.shields.io/badge/AppVersion-2.10.5-informational?style=flat-square) + +A Helm chart to deploy the New Relic Kube Events router + +**Homepage:** + +# Helm installation + +You can install this chart using [`nri-bundle`](https://github.com/newrelic/helm-charts/tree/master/charts/nri-bundle) located in the +[helm-charts repository](https://github.com/newrelic/helm-charts) or directly from this repository by adding this Helm repository: + +```shell +helm repo add nri-kube-events https://newrelic.github.io/nri-kube-events +helm upgrade --install nri-kube-events/nri-kube-events -f your-custom-values.yaml +``` + +## Source Code + +* +* +* + +## Values managed globally + +This chart implements the [New Relic's common Helm library](https://github.com/newrelic/helm-charts/tree/master/library/common-library) which +means that it honors a wide range of defaults and globals common to most New Relic Helm charts. + +Options that can be defined globally include `affinity`, `nodeSelector`, `tolerations`, `proxy` and others. The full list can be found at +[user's guide of the common library](https://github.com/newrelic/helm-charts/blob/master/library/common-library/README.md). + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| affinity | object | `{}` | Sets pod/node affinities. Can be configured also with `global.affinity` | +| agentHTTPTimeout | string | `"30s"` | Amount of time to wait until timeout to send metrics to the metric forwarder | +| cluster | string | `""` | Name of the Kubernetes cluster monitored. Mandatory. Can be configured also with `global.cluster` | +| containerSecurityContext | object | `{}` | Sets security context (at container level). Can be configured also with `global.containerSecurityContext` | +| customAttributes | object | `{}` | Adds extra attributes to the cluster and all the metrics emitted to the backend. Can be configured also with `global.customAttributes` | +| customSecretLicenseKey | string | `""` | In case you don't want to have the license key in you values, this allows you to point to which secret key is the license key located. Can be configured also with `global.customSecretLicenseKey` | +| customSecretName | string | `""` | In case you don't want to have the license key in you values, this allows you to point to a user created secret to get the key from there. Can be configured also with `global.customSecretName` | +| deployment.annotations | object | `{}` | Annotations to add to the Deployment. | +| dnsConfig | object | `{}` | Sets pod's dnsConfig. Can be configured also with `global.dnsConfig` | +| fedramp.enabled | bool | `false` | Enables FedRAMP. Can be configured also with `global.fedramp.enabled` | +| forwarder | object | `{"resources":{}}` | Resources for the forwarder sidecar container | +| fullnameOverride | string | `""` | Override the full name of the release | +| hostNetwork | bool | `false` | Sets pod's hostNetwork. Can be configured also with `global.hostNetwork` | +| images | object | See `values.yaml` | Images used by the chart for the integration and agents | +| images.agent | object | See `values.yaml` | Image for the New Relic Infrastructure Agent sidecar | +| images.integration | object | See `values.yaml` | Image for the New Relic Kubernetes integration | +| images.pullSecrets | list | `[]` | The secrets that are needed to pull images from a custom registry. | +| labels | object | `{}` | Additional labels for chart objects | +| licenseKey | string | `""` | This set this license key to use. Can be configured also with `global.licenseKey` | +| nameOverride | string | `""` | Override the name of the chart | +| nodeSelector | object | `{}` | Sets pod's node selector. Can be configured also with `global.nodeSelector` | +| nrStaging | bool | `false` | Send the metrics to the staging backend. Requires a valid staging license key. Can be configured also with `global.nrStaging` | +| podAnnotations | object | `{}` | Annotations to add to the pod. | +| podLabels | object | `{}` | Additional labels for chart pods | +| podSecurityContext | object | `{}` | Sets security context (at pod level). Can be configured also with `global.podSecurityContext` | +| priorityClassName | string | `""` | Sets pod's priorityClassName. Can be configured also with `global.priorityClassName` | +| proxy | string | `""` | Configures the integration to send all HTTP/HTTPS request through the proxy in that URL. The URL should have a standard format like `https://user:password@hostname:port`. Can be configured also with `global.proxy` | +| rbac.create | bool | `true` | Specifies whether RBAC resources should be created | +| resources | object | `{}` | Resources for the integration container | +| scrapers | object | See `values.yaml` | Configure the various kinds of scrapers that should be run. | +| serviceAccount | object | See `values.yaml` | Settings controlling ServiceAccount creation | +| serviceAccount.create | bool | `true` | Specifies whether a ServiceAccount should be created | +| sinks | object | See `values.yaml` | Configure where will the metrics be written. Mostly for debugging purposes. | +| sinks.newRelicInfra | bool | `true` | The newRelicInfra sink sends all events to New Relic. | +| sinks.stdout | bool | `false` | Enable the stdout sink to also see all events in the logs. | +| tolerations | list | `[]` | Sets pod's tolerations to node taints. Can be configured also with `global.tolerations` | +| verboseLog | bool | `false` | Sets the debug logs to this integration or all integrations if it is set globally. Can be configured also with `global.verboseLog` | + +## Maintainers + +* [juanjjaramillo](https://github.com/juanjjaramillo) +* [csongnr](https://github.com/csongnr) +* [dbudziwojskiNR](https://github.com/dbudziwojskiNR) diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/README.md.gotmpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/README.md.gotmpl new file mode 100644 index 0000000000..e77eb7f14a --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/README.md.gotmpl @@ -0,0 +1,43 @@ +{{ template "chart.header" . }} +{{ template "chart.deprecationWarning" . }} + +{{ template "chart.badgesSection" . }} + +{{ template "chart.description" . }} + +{{ template "chart.homepageLine" . }} + +# Helm installation + +You can install this chart using [`nri-bundle`](https://github.com/newrelic/helm-charts/tree/master/charts/nri-bundle) located in the +[helm-charts repository](https://github.com/newrelic/helm-charts) or directly from this repository by adding this Helm repository: + +```shell +helm repo add nri-kube-events https://newrelic.github.io/nri-kube-events +helm upgrade --install nri-kube-events/nri-kube-events -f your-custom-values.yaml +``` + +{{ template "chart.sourcesSection" . }} + +## Values managed globally + +This chart implements the [New Relic's common Helm library](https://github.com/newrelic/helm-charts/tree/master/library/common-library) which +means that it honors a wide range of defaults and globals common to most New Relic Helm charts. + +Options that can be defined globally include `affinity`, `nodeSelector`, `tolerations`, `proxy` and others. The full list can be found at +[user's guide of the common library](https://github.com/newrelic/helm-charts/blob/master/library/common-library/README.md). + +{{ template "chart.valuesSection" . }} + +{{ if .Maintainers }} +## Maintainers +{{ range .Maintainers }} +{{- if .Name }} +{{- if .Url }} +* [{{ .Name }}]({{ .Url }}) +{{- else }} +* {{ .Name }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/.helmignore b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/.helmignore new file mode 100644 index 0000000000..0e8a0eb36f --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/.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 +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/Chart.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/Chart.yaml new file mode 100644 index 0000000000..f2ee5497ee --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/Chart.yaml @@ -0,0 +1,17 @@ +apiVersion: v2 +description: Provides helpers to provide consistency on all the charts +keywords: +- newrelic +- chart-library +maintainers: +- name: juanjjaramillo + url: https://github.com/juanjjaramillo +- name: csongnr + url: https://github.com/csongnr +- name: dbudziwojskiNR + url: https://github.com/dbudziwojskiNR +- name: kang-makes + url: https://github.com/kang-makes +name: common-library +type: library +version: 1.3.0 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/DEVELOPERS.md b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/DEVELOPERS.md new file mode 100644 index 0000000000..7208c673ec --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/DEVELOPERS.md @@ -0,0 +1,747 @@ +# Functions/templates documented for chart writers +Here is some rough documentation separated by the file that contains the function, the function +name and how to use it. We are not covering functions that start with `_` (e.g. +`newrelic.common.license._licenseKey`) because they are used internally by this library for +other helpers. Helm does not have the concept of "public" or "private" functions/templates so +this is a convention of ours. + +## _naming.tpl +These functions are used to name objects. + +### `newrelic.common.naming.name` +This is the same as the idiomatic `CHART-NAME.name` that is created when you use `helm create`. + +It honors `.Values.nameOverride`. + +Usage: +```mustache +{{ include "newrelic.common.naming.name" . }} +``` + +### `newrelic.common.naming.fullname` +This is the same as the idiomatic `CHART-NAME.fullname` that is created when you use `helm create` + +It honors `.Values.fullnameOverride`. + +Usage: +```mustache +{{ include "newrelic.common.naming.fullname" . }} +``` + +### `newrelic.common.naming.chart` +This is the same as the idiomatic `CHART-NAME.chart` that is created when you use `helm create`. + +It is mostly useless for chart writers. It is used internally for templating the labels but there +is no reason to keep it "private". + +Usage: +```mustache +{{ include "newrelic.common.naming.chart" . }} +``` + +### `newrelic.common.naming.truncateToDNS` +This is a useful template that could be used to trim a string to 63 chars and does not end with a dash (`-`). +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). + +Usage: +```mustache +{{ $nameToTruncate := "a-really-really-really-really-REALLY-long-string-that-should-be-truncated-because-it-is-enought-long-to-brak-something" +{{- $truncatedName := include "newrelic.common.naming.truncateToDNS" $nameToTruncate }} +{{- $truncatedName }} +{{- /* This should print: a-really-really-really-really-REALLY-long-string-that-should-be */ -}} +``` + +### `newrelic.common.naming.truncateToDNSWithSuffix` +This template function is the same as the above but instead of receiving a string you should give a `dict` +with a `name` and a `suffix`. This function will join them with a dash (`-`) and trim the `name` so the +result of `name-suffix` is no more than 63 chars + +Usage: +```mustache +{{ $nameToTruncate := "a-really-really-really-really-REALLY-long-string-that-should-be-truncated-because-it-is-enought-long-to-brak-something" +{{- $suffix := "A-NOT-SO-LONG-SUFFIX" }} +{{- $truncatedName := include "truncateToDNSWithSuffix" (dict "name" $nameToTruncate "suffix" $suffix) }} +{{- $truncatedName }} +{{- /* This should print: a-really-really-really-really-REALLY-long-A-NOT-SO-LONG-SUFFIX */ -}} +``` + + + +## _labels.tpl +### `newrelic.common.labels`, `newrelic.common.labels.selectorLabels` and `newrelic.common.labels.podLabels` +These are functions that are used to label objects. They are configured by this `values.yaml` +```yaml +global: + podLabels: {} # included in all the pods of all the charts that implement this library + labels: {} # included in all the objects of all the charts that implement this library +podLabels: {} # included in all the pods of this chart +labels: {} # included in all the objects of this chart +``` + +label maps are merged from global to local values. + +And chart writer should use them like this: +```mustache +metadata: + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +spec: + selector: + matchLabels: + {{- include "newrelic.common.labels.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "newrelic.common.labels.podLabels" . | nindent 8 }} +``` + +`newrelic.common.labels.podLabels` includes `newrelic.common.labels.selectorLabels` automatically. + + + +## _priority-class-name.tpl +### `newrelic.common.priorityClassName` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + priorityClassName: "" +priorityClassName: "" +``` + +Be careful: chart writers should put an empty string (or any kind of Helm falsiness) for this +library to work properly. If in your values a non-falsy `priorityClassName` is found, the global +one is going to be always ignored. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.priorityClassName" . }} + priorityClassName: {{ . }} + {{- end }} +``` + + + +## _hostnetwork.tpl +### `newrelic.common.hostNetwork` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + hostNetwork: # Note that this is empty (nil) +hostNetwork: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `hostNetwork` is defined, the global one is going to be always ignored. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.hostNetwork" . }} + hostNetwork: {{ . }} + {{- end }} +``` + +### `newrelic.common.hostNetwork.value` +This function is an abstraction of the function above but this returns directly "true" or "false". + +Be careful with using this with an `if` as Helm does evaluate "false" (string) as `true`. + +Usage (example in a pod spec): +```mustache +spec: + hostNetwork: {{ include "newrelic.common.hostNetwork.value" . }} +``` + + + +## _dnsconfig.tpl +### `newrelic.common.dnsConfig` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + dnsConfig: {} +dnsConfig: {} +``` + +Be careful: chart writers should put an empty string (or any kind of Helm falsiness) for this +library to work properly. If in your values a non-falsy `dnsConfig` is found, the global +one is going to be always ignored. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.dnsConfig" . }} + dnsConfig: + {{- . | nindent 4 }} + {{- end }} +``` + + + +## _images.tpl +These functions help us to deal with how images are templated. This allows setting `registries` +where to fetch images globally while being flexible enough to fit in different maps of images +and deployments with one or more images. This is the example of a complex `values.yaml` that +we are going to use during the documentation of these functions: + +```yaml +global: + images: + registry: nexus-3-instance.internal.clients-domain.tld +jobImage: + registry: # defaults to "example.tld" when empty in these examples + repository: ingress-nginx/kube-webhook-certgen + tag: v1.1.1 + pullPolicy: IfNotPresent + pullSecrets: [] +images: + integration: + registry: + repository: newrelic/nri-kube-events + tag: 1.8.0 + pullPolicy: IfNotPresent + agent: + registry: + repository: newrelic/k8s-events-forwarder + tag: 1.22.0 + pullPolicy: IfNotPresent + pullSecrets: [] +``` + +### `newrelic.common.images.image` +This will return a string with the image ready to be downloaded that includes the registry, the image and the tag. +`defaultRegistry` is used to keep `registry` field empty in `values.yaml` so you can override the image using +`global.images.registry`, your local `jobImage.registry` and be able to fallback to a registry that is not `docker.io` +(Or the default repository that the client could have set in the CRI). + +Usage: +```mustache +{{- /* For the integration */}} +{{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.images.agent "context" .) }} +{{- /* For jobImage */}} +{{ include "newrelic.common.images.image" ( dict "defaultRegistry" "example.tld" "imageRoot" .Values.jobImage "context" .) }} +``` + +### `newrelic.common.images.registry` +It returns the registry from the global or local values. You should avoid using this helper to create your image +URL and use `newrelic.common.images.image` instead, but it is there to be used in case it is needed. + +Usage: +```mustache +{{- /* For the integration */}} +{{ include "newrelic.common.images.registry" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.registry" ( dict "imageRoot" .Values.images.agent "context" .) }} +{{- /* For jobImage */}} +{{ include "newrelic.common.images.registry" ( dict "defaultRegistry" "example.tld" "imageRoot" .Values.jobImage "context" .) }} +``` + +### `newrelic.common.images.repository` +It returns the image from the values. You should avoid using this helper to create your image +URL and use `newrelic.common.images.image` instead, but it is there to be used in case it is needed. + +Usage: +```mustache +{{- /* For jobImage */}} +{{ include "newrelic.common.images.repository" ( dict "imageRoot" .Values.jobImage "context" .) }} +{{- /* For the integration */}} +{{ include "newrelic.common.images.repository" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.repository" ( dict "imageRoot" .Values.images.agent "context" .) }} +``` + +### `newrelic.common.images.tag` +It returns the image's tag from the values. You should avoid using this helper to create your image +URL and use `newrelic.common.images.image` instead, but it is there to be used in case it is needed. + +Usage: +```mustache +{{- /* For jobImage */}} +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.jobImage "context" .) }} +{{- /* For the integration */}} +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.images.agent "context" .) }} +``` + +### `newrelic.common.images.renderPullSecrets` +If returns a merged map that contains the pull secrets from the global configuration and the local one. + +Usage: +```mustache +{{- /* For jobImage */}} +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" .Values.jobImage.pullSecrets "context" .) }} +{{- /* For the integration */}} +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" .Values.images.pullSecrets "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" .Values.images.pullSecrets "context" .) }} +``` + + + +## _serviceaccount.tpl +These functions are used to evaluate if the service account should be created, with which name and add annotations to it. + +The functions that the common library has implemented for service accounts are: +* `newrelic.common.serviceAccount.create` +* `newrelic.common.serviceAccount.name` +* `newrelic.common.serviceAccount.annotations` + +Usage: +```mustache +{{- if include "newrelic.common.serviceAccount.create" . -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + {{- with (include "newrelic.common.serviceAccount.annotations" .) }} + annotations: + {{- . | nindent 4 }} + {{- end }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "newrelic.common.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} +{{- end }} +``` + + + +## _affinity.tpl, _nodeselector.tpl and _tolerations.tpl +These three files are almost the same and they follow the idiomatic way of `helm create`. + +Each function also looks if there is a global value like the other helpers. +```yaml +global: + affinity: {} + nodeSelector: {} + tolerations: [] +affinity: {} +nodeSelector: {} +tolerations: [] +``` + +The values here are replaced instead of be merged. If a value at root level is found, the global one is ignored. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.nodeSelector" . }} + nodeSelector: + {{- . | nindent 4 }} + {{- end }} + {{- with include "newrelic.common.affinity" . }} + affinity: + {{- . | nindent 4 }} + {{- end }} + {{- with include "newrelic.common.tolerations" . }} + tolerations: + {{- . | nindent 4 }} + {{- end }} +``` + + + +## _agent-config.tpl +### `newrelic.common.agentConfig.defaults` +This returns a YAML that the agent can use directly as a config that includes other options from the values file like verbose mode, +custom attributes, FedRAMP and such. + +Usage: +```mustache +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include newrelic.common.naming.truncateToDNSWithSuffix (dict "name" (include "newrelic.common.naming.fullname" .) suffix "agent-config") }} + namespace: {{ .Release.Namespace }} +data: + newrelic-infra.yml: |- + # This is the configuration file for the infrastructure agent. See: + # https://docs.newrelic.com/docs/infrastructure/install-infrastructure-agent/configuration/infrastructure-agent-configuration-settings/ + {{- include "newrelic.common.agentConfig.defaults" . | nindent 4 }} +``` + + + +## _cluster.tpl +### `newrelic.common.cluster` +Returns the cluster name + +Usage: +```mustache +{{ include "newrelic.common.cluster" . }} +``` + + + +## _custom-attributes.tpl +### `newrelic.common.customAttributes` +Return custom attributes in YAML format. + +Usage: +```mustache +apiVersion: v1 +kind: ConfigMap +metadata: + name: example +data: + custom-attributes.yaml: | + {{- include "newrelic.common.customAttributes" . | nindent 4 }} + custom-attributes.json: | + {{- include "newrelic.common.customAttributes" . | fromYaml | toJson | nindent 4 }} +``` + + + +## _fedramp.tpl +### `newrelic.common.fedramp.enabled` +Returns true if FedRAMP is enabled or an empty string if not. It can be safely used in conditionals as an empty string is a Helm falsiness. + +Usage: +```mustache +{{ include "newrelic.common.fedramp.enabled" . }} +``` + +### `newrelic.common.fedramp.enabled.value` +Returns true if FedRAMP is enabled or false if not. This is to have the value of FedRAMP ready to be templated. + +Usage: +```mustache +{{ include "newrelic.common.fedramp.enabled.value" . }} +``` + + + +## _license.tpl +### `newrelic.common.license.secretName` and ### `newrelic.common.license.secretKeyName` +Returns the secret and key inside the secret where to read the license key. + +The common library will take care of using a user-provided custom secret or creating a secret that contains the license key. + +To create the secret use `newrelic.common.license.secret`. + +Usage: +```mustache +{{- if and (.Values.controlPlane.enabled) (not (include "newrelic.fargate" .)) }} +apiVersion: v1 +kind: Pod +metadata: + name: example +spec: + containers: + - name: agent + env: + - name: "NRIA_LICENSE_KEY" + valueFrom: + secretKeyRef: + name: {{ include "newrelic.common.license.secretName" . }} + key: {{ include "newrelic.common.license.secretKeyName" . }} +``` + + + +## _license_secret.tpl +### `newrelic.common.license.secret` +This function templates the secret that is used by agents and integrations with the license Key provided by the user. It will +template nothing (empty string) if the user provides a custom pair of secret name and key. + +This template also fails in case the user has not provided any license key or custom secret so no safety checks have to be done +by chart writers. + +You just must have a template with these two lines: +```mustache +{{- /* Common library will take care of creating the secret or not. */ -}} +{{- include "newrelic.common.license.secret" . -}} +``` + + + +## _insights.tpl +### `newrelic.common.insightsKey.secretName` and ### `newrelic.common.insightsKey.secretKeyName` +Returns the secret and key inside the secret where to read the insights key. + +The common library will take care of using a user-provided custom secret or creating a secret that contains the insights key. + +To create the secret use `newrelic.common.insightsKey.secret`. + +Usage: +```mustache +apiVersion: v1 +kind: Pod +metadata: + name: statsd +spec: + containers: + - name: statsd + env: + - name: "INSIGHTS_KEY" + valueFrom: + secretKeyRef: + name: {{ include "newrelic.common.insightsKey.secretName" . }} + key: {{ include "newrelic.common.insightsKey.secretKeyName" . }} +``` + + + +## _insights_secret.tpl +### `newrelic.common.insightsKey.secret` +This function templates the secret that is used by agents and integrations with the insights key provided by the user. It will +template nothing (empty string) if the user provides a custom pair of secret name and key. + +This template also fails in case the user has not provided any insights key or custom secret so no safety checks have to be done +by chart writers. + +You just must have a template with these two lines: +```mustache +{{- /* Common library will take care of creating the secret or not. */ -}} +{{- include "newrelic.common.insightsKey.secret" . -}} +``` + + + +## _userkey.tpl +### `newrelic.common.userKey.secretName` and ### `newrelic.common.userKey.secretKeyName` +Returns the secret and key inside the secret where to read a user key. + +The common library will take care of using a user-provided custom secret or creating a secret that contains the insights key. + +To create the secret use `newrelic.common.userKey.secret`. + +Usage: +```mustache +apiVersion: v1 +kind: Pod +metadata: + name: statsd +spec: + containers: + - name: statsd + env: + - name: "API_KEY" + valueFrom: + secretKeyRef: + name: {{ include "newrelic.common.userKey.secretName" . }} + key: {{ include "newrelic.common.userKey.secretKeyName" . }} +``` + + + +## _userkey_secret.tpl +### `newrelic.common.userKey.secret` +This function templates the secret that is used by agents and integrations with a user key provided by the user. It will +template nothing (empty string) if the user provides a custom pair of secret name and key. + +This template also fails in case the user has not provided any API key or custom secret so no safety checks have to be done +by chart writers. + +You just must have a template with these two lines: +```mustache +{{- /* Common library will take care of creating the secret or not. */ -}} +{{- include "newrelic.common.userKey.secret" . -}} +``` + + + +## _region.tpl +### `newrelic.common.region.validate` +Given a string, return a normalized name for the region if valid. + +This function does not need the context of the chart, only the value to be validated. The region returned +honors the region [definition of the newrelic-client-go implementation](https://github.com/newrelic/newrelic-client-go/blob/cbe3e4cf2b95fd37095bf2ffdc5d61cffaec17e2/pkg/region/region_constants.go#L8-L21) +so (as of 2024/09/14) it returns the region as "US", "EU", "Staging", or "Local". + +In case the region provided does not match these 4, the helper calls `fail` and abort the templating. + +Usage: +```mustache +{{ include "newrelic.common.region.validate" "us" }} +``` + +### `newrelic.common.region` +It reads global and local variables for `region`: +```yaml +global: + region: # Note that this can be empty (nil) or "" (empty string) +region: # Note that this can be empty (nil) or "" (empty string) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in your +values a `region` is defined, the global one is going to be always ignored. + +This function gives protection so it enforces users to give the license key as a value in their +`values.yaml` or specify a global or local `region` value. To understand how the `region` value +works, read the documentation of `newrelic.common.region.validate`. + +The function will change the region from US, EU or Staging based of the license key and the +`nrStaging` toggle. Whichever region is computed from the license/toggle can be overridden by +the `region` value. + +Usage: +```mustache +{{ include "newrelic.common.region" . }} +``` + + + +## _low-data-mode.tpl +### `newrelic.common.lowDataMode` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + lowDataMode: # Note that this is empty (nil) +lowDataMode: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `lowdataMode` is defined, the global one is going to be always ignored. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage: +```mustache +{{ include "newrelic.common.lowDataMode" . }} +``` + + + +## _privileged.tpl +### `newrelic.common.privileged` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + privileged: # Note that this is empty (nil) +privileged: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `privileged` is defined, the global one is going to be always ignored. + +Chart writers could override this and put directly a `true` in the `values.yaml` to override the +default of the common library. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage: +```mustache +{{ include "newrelic.common.privileged" . }} +``` + +### `newrelic.common.privileged.value` +Returns true if privileged mode is enabled or false if not. This is to have the value of privileged ready to be templated. + +Usage: +```mustache +{{ include "newrelic.common.privileged.value" . }} +``` + + + +## _proxy.tpl +### `newrelic.common.proxy` +Returns the proxy URL configured by the user. + +Usage: +```mustache +{{ include "newrelic.common.proxy" . }} +``` + + + +## _security-context.tpl +Use these functions to share the security context among all charts. Useful in clusters that have security enforcing not to +use the root user (like OpenShift) or users that have an admission webhooks. + +The functions are: +* `newrelic.common.securityContext.container` +* `newrelic.common.securityContext.pod` + +Usage: +```mustache +apiVersion: v1 +kind: Pod +metadata: + name: example +spec: + spec: + {{- with include "newrelic.common.securityContext.pod" . }} + securityContext: + {{- . | nindent 8 }} + {{- end }} + + containers: + - name: example + {{- with include "nriKubernetes.securityContext.container" . }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} +``` + + + +## _staging.tpl +### `newrelic.common.nrStaging` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + nrStaging: # Note that this is empty (nil) +nrStaging: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `nrStaging` is defined, the global one is going to be always ignored. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage: +```mustache +{{ include "newrelic.common.nrStaging" . }} +``` + +### `newrelic.common.nrStaging.value` +Returns true if staging is enabled or false if not. This is to have the staging value ready to be templated. + +Usage: +```mustache +{{ include "newrelic.common.nrStaging.value" . }} +``` + + + +## _verbose-log.tpl +### `newrelic.common.verboseLog` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + verboseLog: # Note that this is empty (nil) +verboseLog: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `verboseLog` is defined, the global one is going to be always ignored. + +Usage: +```mustache +{{ include "newrelic.common.verboseLog" . }} +``` + +### `newrelic.common.verboseLog.valueAsBoolean` +Returns true if verbose is enabled or false if not. This is to have the verbose value ready to be templated as a boolean + +Usage: +```mustache +{{ include "newrelic.common.verboseLog.valueAsBoolean" . }} +``` + +### `newrelic.common.verboseLog.valueAsInt` +Returns 1 if verbose is enabled or 0 if not. This is to have the verbose value ready to be templated as an integer + +Usage: +```mustache +{{ include "newrelic.common.verboseLog.valueAsInt" . }} +``` diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/README.md b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/README.md new file mode 100644 index 0000000000..10f08ca677 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/README.md @@ -0,0 +1,106 @@ +# Helm Common library + +The common library is a way to unify the UX through all the Helm charts that implement it. + +The tooling suite that New Relic is huge and growing and this allows to set things globally +and locally for a single chart. + +## Documentation for chart writers + +If you are writing a chart that is going to use this library you can check the [developers guide](/library/common-library/DEVELOPERS.md) to see all +the functions/templates that we have implemented, what they do and how to use them. + +## Values managed globally + +We want to have a seamless experience through all the charts so we created this library that tries to standardize the behaviour +of all the charts. Sadly, because of the complexity of all these integrations, not all the charts behave exactly as expected. + +An example is `newrelic-infrastructure` that ignores `hostNetwork` in the control plane scraper because most of the users has the +control plane listening in the node to `localhost`. + +For each chart that has a special behavior (or further information of the behavior) there is a "chart particularities" section +in its README.md that explains which is the expected behavior. + +At the time of writing this, all the charts from `nri-bundle` except `newrelic-logging` and `synthetics-minion` implements this +library and honors global options as described in this document. + +Here is a list of global options: + +| Global keys | Local keys | Default | Merged[1](#values-managed-globally-1) | Description | +|-------------|------------|---------|--------------------------------------------------|-------------| +| global.cluster | cluster | `""` | | Name of the Kubernetes cluster monitored | +| global.licenseKey | licenseKey | `""` | | This set this license key to use | +| global.customSecretName | customSecretName | `""` | | In case you don't want to have the license key in you values, this allows you to point to a user created secret to get the key from there | +| global.customSecretLicenseKey | customSecretLicenseKey | `""` | | In case you don't want to have the license key in you values, this allows you to point to which secret key is the license key located | +| global.podLabels | podLabels | `{}` | yes | Additional labels for chart pods | +| global.labels | labels | `{}` | yes | Additional labels for chart objects | +| global.priorityClassName | priorityClassName | `""` | | Sets pod's priorityClassName | +| global.hostNetwork | hostNetwork | `false` | | Sets pod's hostNetwork | +| global.dnsConfig | dnsConfig | `{}` | | Sets pod's dnsConfig | +| global.images.registry | See [Further information](#values-managed-globally-2) | `""` | | Changes the registry where to get the images. Useful when there is an internal image cache/proxy | +| global.images.pullSecrets | See [Further information](#values-managed-globally-2) | `[]` | yes | Set secrets to be able to fetch images | +| global.podSecurityContext | podSecurityContext | `{}` | | Sets security context (at pod level) | +| global.containerSecurityContext | containerSecurityContext | `{}` | | Sets security context (at container level) | +| global.affinity | affinity | `{}` | | Sets pod/node affinities | +| global.nodeSelector | nodeSelector | `{}` | | Sets pod's node selector | +| global.tolerations | tolerations | `[]` | | Sets pod's tolerations to node taints | +| global.serviceAccount.create | serviceAccount.create | `true` | | Configures if the service account should be created or not | +| global.serviceAccount.name | serviceAccount.name | name of the release | | Change the name of the service account. This is honored if you disable on this cahrt the creation of the service account so you can use your own. | +| global.serviceAccount.annotations | serviceAccount.annotations | `{}` | yes | Add these annotations to the service account we create | +| global.customAttributes | customAttributes | `{}` | | Adds extra attributes to the cluster and all the metrics emitted to the backend | +| global.fedramp | fedramp | `false` | | Enables FedRAMP | +| global.lowDataMode | lowDataMode | `false` | | Reduces number of metrics sent in order to reduce costs | +| global.privileged | privileged | Depends on the chart | | In each integration it has different behavior. See [Further information](#values-managed-globally-3) but all aims to send less metrics to the backend to try to save costs | +| global.proxy | proxy | `""` | | Configures the integration to send all HTTP/HTTPS request through the proxy in that URL. The URL should have a standard format like `https://user:password@hostname:port` | +| global.nrStaging | nrStaging | `false` | | Send the metrics to the staging backend. Requires a valid staging license key | +| global.verboseLog | verboseLog | `false` | | Sets the debug/trace logs to this integration or all integrations if it is set globally | + +### Further information + +#### 1. Merged + +Merged means that the values from global are not replaced by the local ones. Think in this example: +```yaml +global: + labels: + global: global + hostNetwork: true + nodeSelector: + global: global + +labels: + local: local +nodeSelector: + local: local +hostNetwork: false +``` + +This values will template `hostNetwork` to `false`, a map of labels `{ "global": "global", "local": "local" }` and a `nodeSelector` with +`{ "local": "local" }`. + +As Helm by default merges all the maps it could be confusing that we have two behaviors (merging `labels` and replacing `nodeSelector`) +the `values` from global to local. This is the rationale behind this: +* `hostNetwork` is templated to `false` because is overriding the value defined globally. +* `labels` are merged because the user may want to label all the New Relic pods at once and label other solution pods differently for + clarity' sake. +* `nodeSelector` does not merge as `labels` because could make it harder to overwrite/delete a selector that comes from global because + of the logic that Helm follows merging maps. + + +#### 2. Fine grain registries + +Some charts only have 1 image while others that can have 2 or more images. The local path for the registry can change depending +on the chart itself. + +As this is mostly unique per helm chart, you should take a look to the chart's values table (or directly to the `values.yaml` file to see all the +images that you can change. + +This should only be needed if you have an advanced setup that forces you to have granularity enough to force a proxy/cache registry per integration. + + + +#### 3. Privileged mode + +By default, from the common library, the privileged mode is set to false. But most of the helm charts require this to be true to fetch more +metrics so could see a true in some charts. The consequences of the privileged mode differ from one chart to another so for each chart that +honors the privileged mode toggle should be a section in the README explaining which is the behavior with it enabled or disabled. diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_affinity.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_affinity.tpl new file mode 100644 index 0000000000..1b2636754e --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_affinity.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod affinity */ -}} +{{- define "newrelic.common.affinity" -}} + {{- if .Values.affinity -}} + {{- toYaml .Values.affinity -}} + {{- else if .Values.global -}} + {{- if .Values.global.affinity -}} + {{- toYaml .Values.global.affinity -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_agent-config.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_agent-config.tpl new file mode 100644 index 0000000000..9c32861a02 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_agent-config.tpl @@ -0,0 +1,26 @@ +{{/* +This helper should return the defaults that all agents should have +*/}} +{{- define "newrelic.common.agentConfig.defaults" -}} +{{- if include "newrelic.common.verboseLog" . }} +log: + level: trace +{{- end }} + +{{- if (include "newrelic.common.nrStaging" . ) }} +staging: true +{{- end }} + +{{- with include "newrelic.common.proxy" . }} +proxy: {{ . | quote }} +{{- end }} + +{{- with include "newrelic.common.fedramp.enabled" . }} +fedramp: {{ . }} +{{- end }} + +{{- with fromYaml ( include "newrelic.common.customAttributes" . ) }} +custom_attributes: + {{- toYaml . | nindent 2 }} +{{- end }} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_cluster.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_cluster.tpl new file mode 100644 index 0000000000..0197dd35a3 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_cluster.tpl @@ -0,0 +1,15 @@ +{{/* +Return the cluster +*/}} +{{- define "newrelic.common.cluster" -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} + +{{- if .Values.cluster -}} + {{- .Values.cluster -}} +{{- else if $global.cluster -}} + {{- $global.cluster -}} +{{- else -}} + {{ fail "There is not cluster name definition set neither in `.global.cluster' nor `.cluster' in your values.yaml. Cluster name is required." }} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_custom-attributes.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_custom-attributes.tpl new file mode 100644 index 0000000000..92020719c3 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_custom-attributes.tpl @@ -0,0 +1,17 @@ +{{/* +This will render custom attributes as a YAML ready to be templated or be used with `fromYaml`. +*/}} +{{- define "newrelic.common.customAttributes" -}} +{{- $customAttributes := dict -}} + +{{- $global := index .Values "global" | default dict -}} +{{- if $global.customAttributes -}} +{{- $customAttributes = mergeOverwrite $customAttributes $global.customAttributes -}} +{{- end -}} + +{{- if .Values.customAttributes -}} +{{- $customAttributes = mergeOverwrite $customAttributes .Values.customAttributes -}} +{{- end -}} + +{{- toYaml $customAttributes -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_dnsconfig.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_dnsconfig.tpl new file mode 100644 index 0000000000..d4e40aa8af --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_dnsconfig.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod dnsConfig */ -}} +{{- define "newrelic.common.dnsConfig" -}} + {{- if .Values.dnsConfig -}} + {{- toYaml .Values.dnsConfig -}} + {{- else if .Values.global -}} + {{- if .Values.global.dnsConfig -}} + {{- toYaml .Values.global.dnsConfig -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_fedramp.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_fedramp.tpl new file mode 100644 index 0000000000..9df8d6b5e9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_fedramp.tpl @@ -0,0 +1,25 @@ +{{- /* Defines the fedRAMP flag */ -}} +{{- define "newrelic.common.fedramp.enabled" -}} + {{- if .Values.fedramp -}} + {{- if .Values.fedramp.enabled -}} + {{- .Values.fedramp.enabled -}} + {{- end -}} + {{- else if .Values.global -}} + {{- if .Values.global.fedramp -}} + {{- if .Values.global.fedramp.enabled -}} + {{- .Values.global.fedramp.enabled -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} + + + +{{- /* Return FedRAMP value directly ready to be templated */ -}} +{{- define "newrelic.common.fedramp.enabled.value" -}} +{{- if include "newrelic.common.fedramp.enabled" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_hostnetwork.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_hostnetwork.tpl new file mode 100644 index 0000000000..4cf017ef7e --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_hostnetwork.tpl @@ -0,0 +1,39 @@ +{{- /* +Abstraction of the hostNetwork toggle. +This helper allows to override the global `.global.hostNetwork` with the value of `.hostNetwork`. +Returns "true" if `hostNetwork` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.hostNetwork" -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} + +{{- /* +`get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs + +We also want only to return when this is true, returning `false` here will template "false" (string) when doing +an `(include "newrelic.common.hostNetwork" .)`, which is not an "empty string" so it is `true` if it is used +as an evaluation somewhere else. +*/ -}} +{{- if get .Values "hostNetwork" | kindIs "bool" -}} + {{- if .Values.hostNetwork -}} + {{- .Values.hostNetwork -}} + {{- end -}} +{{- else if get $global "hostNetwork" | kindIs "bool" -}} + {{- if $global.hostNetwork -}} + {{- $global.hostNetwork -}} + {{- end -}} +{{- end -}} +{{- end -}} + + +{{- /* +Abstraction of the hostNetwork toggle. +This helper abstracts the function "newrelic.common.hostNetwork" to return true or false directly. +*/ -}} +{{- define "newrelic.common.hostNetwork.value" -}} +{{- if include "newrelic.common.hostNetwork" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_images.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_images.tpl new file mode 100644 index 0000000000..d4fb432905 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_images.tpl @@ -0,0 +1,94 @@ +{{- /* +Return the proper image name +{{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.path.to.the.image "defaultRegistry" "your.private.registry.tld" "context" .) }} +*/ -}} +{{- define "newrelic.common.images.image" -}} + {{- $registryName := include "newrelic.common.images.registry" ( dict "imageRoot" .imageRoot "defaultRegistry" .defaultRegistry "context" .context ) -}} + {{- $repositoryName := include "newrelic.common.images.repository" .imageRoot -}} + {{- $tag := include "newrelic.common.images.tag" ( dict "imageRoot" .imageRoot "context" .context) -}} + + {{- if $registryName -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag | quote -}} + {{- else -}} + {{- printf "%s:%s" $repositoryName $tag | quote -}} + {{- end -}} +{{- end -}} + + + +{{- /* +Return the proper image registry +{{ include "newrelic.common.images.registry" ( dict "imageRoot" .Values.path.to.the.image "defaultRegistry" "your.private.registry.tld" "context" .) }} +*/ -}} +{{- define "newrelic.common.images.registry" -}} +{{- $globalRegistry := "" -}} +{{- if .context.Values.global -}} + {{- if .context.Values.global.images -}} + {{- with .context.Values.global.images.registry -}} + {{- $globalRegistry = . -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- $localRegistry := "" -}} +{{- if .imageRoot.registry -}} + {{- $localRegistry = .imageRoot.registry -}} +{{- end -}} + +{{- $registry := $localRegistry | default $globalRegistry | default .defaultRegistry -}} +{{- if $registry -}} + {{- $registry -}} +{{- end -}} +{{- end -}} + + + +{{- /* +Return the proper image repository +{{ include "newrelic.common.images.repository" .Values.path.to.the.image }} +*/ -}} +{{- define "newrelic.common.images.repository" -}} + {{- .repository -}} +{{- end -}} + + + +{{- /* +Return the proper image tag +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.path.to.the.image "context" .) }} +*/ -}} +{{- define "newrelic.common.images.tag" -}} + {{- .imageRoot.tag | default .context.Chart.AppVersion | toString -}} +{{- end -}} + + + +{{- /* +Return the proper Image Pull Registry Secret Names evaluating values as templates +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" (list .Values.path.to.the.images.pullSecrets1, .Values.path.to.the.images.pullSecrets2) "context" .) }} +*/ -}} +{{- define "newrelic.common.images.renderPullSecrets" -}} + {{- $flatlist := list }} + + {{- if .context.Values.global -}} + {{- if .context.Values.global.images -}} + {{- if .context.Values.global.images.pullSecrets -}} + {{- range .context.Values.global.images.pullSecrets -}} + {{- $flatlist = append $flatlist . -}} + {{- end -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- range .pullSecrets -}} + {{- if not (empty .) -}} + {{- range . -}} + {{- $flatlist = append $flatlist . -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- if $flatlist -}} + {{- toYaml $flatlist -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_insights.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_insights.tpl new file mode 100644 index 0000000000..895c377326 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_insights.tpl @@ -0,0 +1,56 @@ +{{/* +Return the name of the secret holding the Insights Key. +*/}} +{{- define "newrelic.common.insightsKey.secretName" -}} +{{- $default := include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "insightskey" ) -}} +{{- include "newrelic.common.insightsKey._customSecretName" . | default $default -}} +{{- end -}} + +{{/* +Return the name key for the Insights Key inside the secret. +*/}} +{{- define "newrelic.common.insightsKey.secretKeyName" -}} +{{- include "newrelic.common.insightsKey._customSecretKey" . | default "insightsKey" -}} +{{- end -}} + +{{/* +Return local insightsKey if set, global otherwise. +This helper is for internal use. +*/}} +{{- define "newrelic.common.insightsKey._licenseKey" -}} +{{- if .Values.insightsKey -}} + {{- .Values.insightsKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.insightsKey -}} + {{- .Values.global.insightsKey -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name of the secret holding the Insights Key. +This helper is for internal use. +*/}} +{{- define "newrelic.common.insightsKey._customSecretName" -}} +{{- if .Values.customInsightsKeySecretName -}} + {{- .Values.customInsightsKeySecretName -}} +{{- else if .Values.global -}} + {{- if .Values.global.customInsightsKeySecretName -}} + {{- .Values.global.customInsightsKeySecretName -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name key for the Insights Key inside the secret. +This helper is for internal use. +*/}} +{{- define "newrelic.common.insightsKey._customSecretKey" -}} +{{- if .Values.customInsightsKeySecretKey -}} + {{- .Values.customInsightsKeySecretKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.customInsightsKeySecretKey }} + {{- .Values.global.customInsightsKeySecretKey -}} + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_insights_secret.yaml.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_insights_secret.yaml.tpl new file mode 100644 index 0000000000..556caa6ca6 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_insights_secret.yaml.tpl @@ -0,0 +1,21 @@ +{{/* +Renders the insights key secret if user has not specified a custom secret. +*/}} +{{- define "newrelic.common.insightsKey.secret" }} +{{- if not (include "newrelic.common.insightsKey._customSecretName" .) }} +{{- /* Fail if licenseKey is empty and required: */ -}} +{{- if not (include "newrelic.common.insightsKey._licenseKey" .) }} + {{- fail "You must specify a insightsKey or a customInsightsSecretName containing it" }} +{{- end }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "newrelic.common.insightsKey.secretName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +data: + {{ include "newrelic.common.insightsKey.secretKeyName" . }}: {{ include "newrelic.common.insightsKey._licenseKey" . | b64enc }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_labels.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_labels.tpl new file mode 100644 index 0000000000..b025948285 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_labels.tpl @@ -0,0 +1,54 @@ +{{/* +This will render the labels that should be used in all the manifests used by the helm chart. +*/}} +{{- define "newrelic.common.labels" -}} +{{- $global := index .Values "global" | default dict -}} + +{{- $chart := dict "helm.sh/chart" (include "newrelic.common.naming.chart" . ) -}} +{{- $managedBy := dict "app.kubernetes.io/managed-by" .Release.Service -}} +{{- $selectorLabels := fromYaml (include "newrelic.common.labels.selectorLabels" . ) -}} + +{{- $labels := mustMergeOverwrite $chart $managedBy $selectorLabels -}} +{{- if .Chart.AppVersion -}} +{{- $labels = mustMergeOverwrite $labels (dict "app.kubernetes.io/version" .Chart.AppVersion) -}} +{{- end -}} + +{{- $globalUserLabels := $global.labels | default dict -}} +{{- $localUserLabels := .Values.labels | default dict -}} + +{{- $labels = mustMergeOverwrite $labels $globalUserLabels $localUserLabels -}} + +{{- toYaml $labels -}} +{{- end -}} + + + +{{/* +This will render the labels that should be used in deployments/daemonsets template pods as a selector. +*/}} +{{- define "newrelic.common.labels.selectorLabels" -}} +{{- $name := dict "app.kubernetes.io/name" ( include "newrelic.common.naming.name" . ) -}} +{{- $instance := dict "app.kubernetes.io/instance" .Release.Name -}} + +{{- $selectorLabels := mustMergeOverwrite $name $instance -}} + +{{- toYaml $selectorLabels -}} +{{- end }} + + + +{{/* +Pod labels +*/}} +{{- define "newrelic.common.labels.podLabels" -}} +{{- $selectorLabels := fromYaml (include "newrelic.common.labels.selectorLabels" . ) -}} + +{{- $global := index .Values "global" | default dict -}} +{{- $globalPodLabels := $global.podLabels | default dict }} + +{{- $localPodLabels := .Values.podLabels | default dict }} + +{{- $podLabels := mustMergeOverwrite $selectorLabels $globalPodLabels $localPodLabels -}} + +{{- toYaml $podLabels -}} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_license.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_license.tpl new file mode 100644 index 0000000000..cb349f6bb6 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_license.tpl @@ -0,0 +1,68 @@ +{{/* +Return the name of the secret holding the License Key. +*/}} +{{- define "newrelic.common.license.secretName" -}} +{{- $default := include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "license" ) -}} +{{- include "newrelic.common.license._customSecretName" . | default $default -}} +{{- end -}} + +{{/* +Return the name key for the License Key inside the secret. +*/}} +{{- define "newrelic.common.license.secretKeyName" -}} +{{- include "newrelic.common.license._customSecretKey" . | default "licenseKey" -}} +{{- end -}} + +{{/* +Return local licenseKey if set, global otherwise. +This helper is for internal use. +*/}} +{{- define "newrelic.common.license._licenseKey" -}} +{{- if .Values.licenseKey -}} + {{- .Values.licenseKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.licenseKey -}} + {{- .Values.global.licenseKey -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name of the secret holding the License Key. +This helper is for internal use. +*/}} +{{- define "newrelic.common.license._customSecretName" -}} +{{- if .Values.customSecretName -}} + {{- .Values.customSecretName -}} +{{- else if .Values.global -}} + {{- if .Values.global.customSecretName -}} + {{- .Values.global.customSecretName -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name key for the License Key inside the secret. +This helper is for internal use. +*/}} +{{- define "newrelic.common.license._customSecretKey" -}} +{{- if .Values.customSecretLicenseKey -}} + {{- .Values.customSecretLicenseKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.customSecretLicenseKey }} + {{- .Values.global.customSecretLicenseKey -}} + {{- end -}} +{{- end -}} +{{- end -}} + + + +{{/* +Return empty string (falsehood) or "true" if the user set a custom secret for the license. +This helper is for internal use. +*/}} +{{- define "newrelic.common.license._usesCustomSecret" -}} +{{- if or (include "newrelic.common.license._customSecretName" .) (include "newrelic.common.license._customSecretKey" .) -}} +true +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_license_secret.yaml.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_license_secret.yaml.tpl new file mode 100644 index 0000000000..610a0a3370 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_license_secret.yaml.tpl @@ -0,0 +1,21 @@ +{{/* +Renders the license key secret if user has not specified a custom secret. +*/}} +{{- define "newrelic.common.license.secret" }} +{{- if not (include "newrelic.common.license._customSecretName" .) }} +{{- /* Fail if licenseKey is empty and required: */ -}} +{{- if not (include "newrelic.common.license._licenseKey" .) }} + {{- fail "You must specify a licenseKey or a customSecretName containing it" }} +{{- end }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "newrelic.common.license.secretName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +data: + {{ include "newrelic.common.license.secretKeyName" . }}: {{ include "newrelic.common.license._licenseKey" . | b64enc }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_low-data-mode.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_low-data-mode.tpl new file mode 100644 index 0000000000..3dd55ef2ff --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_low-data-mode.tpl @@ -0,0 +1,26 @@ +{{- /* +Abstraction of the lowDataMode toggle. +This helper allows to override the global `.global.lowDataMode` with the value of `.lowDataMode`. +Returns "true" if `lowDataMode` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.lowDataMode" -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if (get .Values "lowDataMode" | kindIs "bool") -}} + {{- if .Values.lowDataMode -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.lowDataMode" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.lowDataMode -}} + {{- end -}} +{{- else -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "lowDataMode" | kindIs "bool" -}} + {{- if $global.lowDataMode -}} + {{- $global.lowDataMode -}} + {{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_naming.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_naming.tpl new file mode 100644 index 0000000000..19fa92648c --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_naming.tpl @@ -0,0 +1,73 @@ +{{/* +This is an function to be called directly with a string just to truncate strings to +63 chars because some Kubernetes name fields are limited to that. +*/}} +{{- define "newrelic.common.naming.truncateToDNS" -}} +{{- . | trunc 63 | trimSuffix "-" }} +{{- end }} + + + +{{- /* +Given a name and a suffix returns a 'DNS Valid' which always include the suffix, truncating the name if needed. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If suffix is too long it gets truncated but it always takes precedence over name, so a 63 chars suffix would suppress the name. +Usage: +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" "" "suffix" "my-suffix" ) }} +*/ -}} +{{- define "newrelic.common.naming.truncateToDNSWithSuffix" -}} +{{- $suffix := (include "newrelic.common.naming.truncateToDNS" .suffix) -}} +{{- $maxLen := (max (sub 63 (add1 (len $suffix))) 0) -}} {{- /* We prepend "-" to the suffix so an additional character is needed */ -}} + +{{- $newName := .name | trunc ($maxLen | int) | trimSuffix "-" -}} +{{- if $newName -}} +{{- printf "%s-%s" $newName $suffix -}} +{{- else -}} +{{ $suffix }} +{{- end -}} + +{{- end -}} + + + +{{/* +Expand the name of the chart. +Uses the Chart name by default if nameOverride is not set. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "newrelic.common.naming.name" -}} +{{- $name := .Values.nameOverride | default .Chart.Name -}} +{{- include "newrelic.common.naming.truncateToDNS" $name -}} +{{- end }} + + + +{{/* +Create a default fully qualified app name. +By default the full name will be "" just in if it has the chart name included in that, if not +it will be concatenated like "-". This could change if fullnameOverride or +nameOverride are set. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "newrelic.common.naming.fullname" -}} +{{- $name := include "newrelic.common.naming.name" . -}} + +{{- if .Values.fullnameOverride -}} + {{- $name = .Values.fullnameOverride -}} +{{- else if not (contains $name .Release.Name) -}} + {{- $name = printf "%s-%s" .Release.Name $name -}} +{{- end -}} + +{{- include "newrelic.common.naming.truncateToDNS" $name -}} + +{{- end -}} + + + +{{/* +Create chart name and version as used by the chart label. +This function should not be used for naming objects. Use "common.naming.{name,fullname}" instead. +*/}} +{{- define "newrelic.common.naming.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_nodeselector.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_nodeselector.tpl new file mode 100644 index 0000000000..d488873412 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_nodeselector.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod nodeSelector */ -}} +{{- define "newrelic.common.nodeSelector" -}} + {{- if .Values.nodeSelector -}} + {{- toYaml .Values.nodeSelector -}} + {{- else if .Values.global -}} + {{- if .Values.global.nodeSelector -}} + {{- toYaml .Values.global.nodeSelector -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_priority-class-name.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_priority-class-name.tpl new file mode 100644 index 0000000000..50182b7343 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_priority-class-name.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the pod priorityClassName */ -}} +{{- define "newrelic.common.priorityClassName" -}} + {{- if .Values.priorityClassName -}} + {{- .Values.priorityClassName -}} + {{- else if .Values.global -}} + {{- if .Values.global.priorityClassName -}} + {{- .Values.global.priorityClassName -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_privileged.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_privileged.tpl new file mode 100644 index 0000000000..f3ae814dd9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_privileged.tpl @@ -0,0 +1,28 @@ +{{- /* +This is a helper that returns whether the chart should assume the user is fine deploying privileged pods. +*/ -}} +{{- define "newrelic.common.privileged" -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists. */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if get .Values "privileged" | kindIs "bool" -}} + {{- if .Values.privileged -}} + {{- .Values.privileged -}} + {{- end -}} +{{- else if get $global "privileged" | kindIs "bool" -}} + {{- if $global.privileged -}} + {{- $global.privileged -}} + {{- end -}} +{{- end -}} +{{- end -}} + + + +{{- /* Return directly "true" or "false" based in the exist of "newrelic.common.privileged" */ -}} +{{- define "newrelic.common.privileged.value" -}} +{{- if include "newrelic.common.privileged" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_proxy.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_proxy.tpl new file mode 100644 index 0000000000..60f34c7ec1 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_proxy.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the proxy */ -}} +{{- define "newrelic.common.proxy" -}} + {{- if .Values.proxy -}} + {{- .Values.proxy -}} + {{- else if .Values.global -}} + {{- if .Values.global.proxy -}} + {{- .Values.global.proxy -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_region.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_region.tpl new file mode 100644 index 0000000000..bdcacf3235 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_region.tpl @@ -0,0 +1,74 @@ +{{/* +Return the region that is being used by the user +*/}} +{{- define "newrelic.common.region" -}} +{{- if and (include "newrelic.common.license._usesCustomSecret" .) (not (include "newrelic.common.region._fromValues" .)) -}} + {{- fail "This Helm Chart is not able to compute the region. You must specify a .global.region or .region if the license is set using a custom secret." -}} +{{- end -}} + +{{- /* Defaults */ -}} +{{- $region := "us" -}} +{{- if include "newrelic.common.nrStaging" . -}} + {{- $region = "staging" -}} +{{- else if include "newrelic.common.region._isEULicenseKey" . -}} + {{- $region = "eu" -}} +{{- end -}} + +{{- include "newrelic.common.region.validate" (include "newrelic.common.region._fromValues" . | default $region ) -}} +{{- end -}} + + + +{{/* +Returns the region from the values if valid. This only return the value from the `values.yaml`. +More intelligence should be used to compute the region. + +Usage: `include "newrelic.common.region.validate" "us"` +*/}} +{{- define "newrelic.common.region.validate" -}} +{{- /* Ref: https://github.com/newrelic/newrelic-client-go/blob/cbe3e4cf2b95fd37095bf2ffdc5d61cffaec17e2/pkg/region/region_constants.go#L8-L21 */ -}} +{{- $region := . | lower -}} +{{- if eq $region "us" -}} + US +{{- else if eq $region "eu" -}} + EU +{{- else if eq $region "staging" -}} + Staging +{{- else if eq $region "local" -}} + Local +{{- else -}} + {{- fail (printf "the region provided is not valid: %s not in \"US\" \"EU\" \"Staging\" \"Local\"" .) -}} +{{- end -}} +{{- end -}} + + + +{{/* +Returns the region from the values. This only return the value from the `values.yaml`. +More intelligence should be used to compute the region. +This helper is for internal use. +*/}} +{{- define "newrelic.common.region._fromValues" -}} +{{- if .Values.region -}} + {{- .Values.region -}} +{{- else if .Values.global -}} + {{- if .Values.global.region -}} + {{- .Values.global.region -}} + {{- end -}} +{{- end -}} +{{- end -}} + + + +{{/* +Return empty string (falsehood) or "true" if the license is for EU region. +This helper is for internal use. +*/}} +{{- define "newrelic.common.region._isEULicenseKey" -}} +{{- if not (include "newrelic.common.license._usesCustomSecret" .) -}} + {{- $license := include "newrelic.common.license._licenseKey" . -}} + {{- if hasPrefix "eu" $license -}} + true + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_security-context.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_security-context.tpl new file mode 100644 index 0000000000..9edfcabfd0 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_security-context.tpl @@ -0,0 +1,23 @@ +{{- /* Defines the container securityContext context */ -}} +{{- define "newrelic.common.securityContext.container" -}} +{{- $global := index .Values "global" | default dict -}} + +{{- if .Values.containerSecurityContext -}} + {{- toYaml .Values.containerSecurityContext -}} +{{- else if $global.containerSecurityContext -}} + {{- toYaml $global.containerSecurityContext -}} +{{- end -}} +{{- end -}} + + + +{{- /* Defines the pod securityContext context */ -}} +{{- define "newrelic.common.securityContext.pod" -}} +{{- $global := index .Values "global" | default dict -}} + +{{- if .Values.podSecurityContext -}} + {{- toYaml .Values.podSecurityContext -}} +{{- else if $global.podSecurityContext -}} + {{- toYaml $global.podSecurityContext -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_serviceaccount.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_serviceaccount.tpl new file mode 100644 index 0000000000..2d352f6ea9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_serviceaccount.tpl @@ -0,0 +1,90 @@ +{{- /* Defines if the service account has to be created or not */ -}} +{{- define "newrelic.common.serviceAccount.create" -}} +{{- $valueFound := false -}} + +{{- /* Look for a global creation of a service account */ -}} +{{- if get .Values "serviceAccount" | kindIs "map" -}} + {{- if (get .Values.serviceAccount "create" | kindIs "bool") -}} + {{- $valueFound = true -}} + {{- if .Values.serviceAccount.create -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.serviceAccount.name" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.serviceAccount.create -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- /* Look for a local creation of a service account */ -}} +{{- if not $valueFound -}} + {{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} + {{- $global := index .Values "global" | default dict -}} + {{- if get $global "serviceAccount" | kindIs "map" -}} + {{- if get $global.serviceAccount "create" | kindIs "bool" -}} + {{- $valueFound = true -}} + {{- if $global.serviceAccount.create -}} + {{- $global.serviceAccount.create -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- /* In case no serviceAccount value has been found, default to "true" */ -}} +{{- if not $valueFound -}} +true +{{- end -}} +{{- end -}} + + + +{{- /* Defines the name of the service account */ -}} +{{- define "newrelic.common.serviceAccount.name" -}} +{{- $localServiceAccount := "" -}} +{{- if get .Values "serviceAccount" | kindIs "map" -}} + {{- if (get .Values.serviceAccount "name" | kindIs "string") -}} + {{- $localServiceAccount = .Values.serviceAccount.name -}} + {{- end -}} +{{- end -}} + +{{- $globalServiceAccount := "" -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "serviceAccount" | kindIs "map" -}} + {{- if get $global.serviceAccount "name" | kindIs "string" -}} + {{- $globalServiceAccount = $global.serviceAccount.name -}} + {{- end -}} +{{- end -}} + +{{- if (include "newrelic.common.serviceAccount.create" .) -}} + {{- $localServiceAccount | default $globalServiceAccount | default (include "newrelic.common.naming.fullname" .) -}} +{{- else -}} + {{- $localServiceAccount | default $globalServiceAccount | default "default" -}} +{{- end -}} +{{- end -}} + + + +{{- /* Merge the global and local annotations for the service account */ -}} +{{- define "newrelic.common.serviceAccount.annotations" -}} +{{- $localServiceAccount := dict -}} +{{- if get .Values "serviceAccount" | kindIs "map" -}} + {{- if get .Values.serviceAccount "annotations" -}} + {{- $localServiceAccount = .Values.serviceAccount.annotations -}} + {{- end -}} +{{- end -}} + +{{- $globalServiceAccount := dict -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "serviceAccount" | kindIs "map" -}} + {{- if get $global.serviceAccount "annotations" -}} + {{- $globalServiceAccount = $global.serviceAccount.annotations -}} + {{- end -}} +{{- end -}} + +{{- $merged := mustMergeOverwrite $globalServiceAccount $localServiceAccount -}} + +{{- if $merged -}} + {{- toYaml $merged -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_staging.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_staging.tpl new file mode 100644 index 0000000000..bd9ad09bb9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_staging.tpl @@ -0,0 +1,39 @@ +{{- /* +Abstraction of the nrStaging toggle. +This helper allows to override the global `.global.nrStaging` with the value of `.nrStaging`. +Returns "true" if `nrStaging` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.nrStaging" -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if (get .Values "nrStaging" | kindIs "bool") -}} + {{- if .Values.nrStaging -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.nrStaging" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.nrStaging -}} + {{- end -}} +{{- else -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "nrStaging" | kindIs "bool" -}} + {{- if $global.nrStaging -}} + {{- $global.nrStaging -}} + {{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} + + + +{{- /* +Returns "true" of "false" directly instead of empty string (Helm falsiness) based on the exit of "newrelic.common.nrStaging" +*/ -}} +{{- define "newrelic.common.nrStaging.value" -}} +{{- if include "newrelic.common.nrStaging" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_tolerations.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_tolerations.tpl new file mode 100644 index 0000000000..e016b38e27 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_tolerations.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod tolerations */ -}} +{{- define "newrelic.common.tolerations" -}} + {{- if .Values.tolerations -}} + {{- toYaml .Values.tolerations -}} + {{- else if .Values.global -}} + {{- if .Values.global.tolerations -}} + {{- toYaml .Values.global.tolerations -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_userkey.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_userkey.tpl new file mode 100644 index 0000000000..982ea8e09d --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_userkey.tpl @@ -0,0 +1,56 @@ +{{/* +Return the name of the secret holding the API Key. +*/}} +{{- define "newrelic.common.userKey.secretName" -}} +{{- $default := include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "userkey" ) -}} +{{- include "newrelic.common.userKey._customSecretName" . | default $default -}} +{{- end -}} + +{{/* +Return the name key for the API Key inside the secret. +*/}} +{{- define "newrelic.common.userKey.secretKeyName" -}} +{{- include "newrelic.common.userKey._customSecretKey" . | default "userKey" -}} +{{- end -}} + +{{/* +Return local API Key if set, global otherwise. +This helper is for internal use. +*/}} +{{- define "newrelic.common.userKey._userKey" -}} +{{- if .Values.userKey -}} + {{- .Values.userKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.userKey -}} + {{- .Values.global.userKey -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name of the secret holding the API Key. +This helper is for internal use. +*/}} +{{- define "newrelic.common.userKey._customSecretName" -}} +{{- if .Values.customUserKeySecretName -}} + {{- .Values.customUserKeySecretName -}} +{{- else if .Values.global -}} + {{- if .Values.global.customUserKeySecretName -}} + {{- .Values.global.customUserKeySecretName -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name key for the API Key inside the secret. +This helper is for internal use. +*/}} +{{- define "newrelic.common.userKey._customSecretKey" -}} +{{- if .Values.customUserKeySecretKey -}} + {{- .Values.customUserKeySecretKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.customUserKeySecretKey }} + {{- .Values.global.customUserKeySecretKey -}} + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_userkey_secret.yaml.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_userkey_secret.yaml.tpl new file mode 100644 index 0000000000..b979856548 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_userkey_secret.yaml.tpl @@ -0,0 +1,21 @@ +{{/* +Renders the user key secret if user has not specified a custom secret. +*/}} +{{- define "newrelic.common.userKey.secret" }} +{{- if not (include "newrelic.common.userKey._customSecretName" .) }} +{{- /* Fail if user key is empty and required: */ -}} +{{- if not (include "newrelic.common.userKey._userKey" .) }} + {{- fail "You must specify a userKey or a customUserKeySecretName containing it" }} +{{- end }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "newrelic.common.userKey.secretName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +data: + {{ include "newrelic.common.userKey.secretKeyName" . }}: {{ include "newrelic.common.userKey._userKey" . | b64enc }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_verbose-log.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_verbose-log.tpl new file mode 100644 index 0000000000..2286d46815 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/templates/_verbose-log.tpl @@ -0,0 +1,54 @@ +{{- /* +Abstraction of the verbose toggle. +This helper allows to override the global `.global.verboseLog` with the value of `.verboseLog`. +Returns "true" if `verbose` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.verboseLog" -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if (get .Values "verboseLog" | kindIs "bool") -}} + {{- if .Values.verboseLog -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.verboseLog" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.verboseLog -}} + {{- end -}} +{{- else -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "verboseLog" | kindIs "bool" -}} + {{- if $global.verboseLog -}} + {{- $global.verboseLog -}} + {{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} + + + +{{- /* +Abstraction of the verbose toggle. +This helper abstracts the function "newrelic.common.verboseLog" to return true or false directly. +*/ -}} +{{- define "newrelic.common.verboseLog.valueAsBoolean" -}} +{{- if include "newrelic.common.verboseLog" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} + + + +{{- /* +Abstraction of the verbose toggle. +This helper abstracts the function "newrelic.common.verboseLog" to return 1 or 0 directly. +*/ -}} +{{- define "newrelic.common.verboseLog.valueAsInt" -}} +{{- if include "newrelic.common.verboseLog" . -}} +1 +{{- else -}} +0 +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/values.yaml new file mode 100644 index 0000000000..75e2d112ad --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/charts/common-library/values.yaml @@ -0,0 +1 @@ +# values are not needed for the library chart, however this file is still needed for helm lint to work. diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/ci/test-bare-minimum-values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/ci/test-bare-minimum-values.yaml new file mode 100644 index 0000000000..3fb7df0506 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/ci/test-bare-minimum-values.yaml @@ -0,0 +1,3 @@ +global: + licenseKey: 1234567890abcdef1234567890abcdef12345678 + cluster: test-cluster diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/ci/test-custom-attributes-as-map.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/ci/test-custom-attributes-as-map.yaml new file mode 100644 index 0000000000..9fec33dc63 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/ci/test-custom-attributes-as-map.yaml @@ -0,0 +1,12 @@ +global: + licenseKey: 1234567890abcdef1234567890abcdef12345678 + cluster: test-cluster + +customAttributes: + test_tag_label: test_tag_value + +image: + kubeEvents: + repository: e2e/nri-kube-events + tag: test + pullPolicy: IfNotPresent diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/ci/test-custom-attributes-as-string.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/ci/test-custom-attributes-as-string.yaml new file mode 100644 index 0000000000..e12cba339c --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/ci/test-custom-attributes-as-string.yaml @@ -0,0 +1,11 @@ +global: + licenseKey: 1234567890abcdef1234567890abcdef12345678 + cluster: test-cluster + +customAttributes: '{"test_tag_label": "test_tag_value"}' + +image: + kubeEvents: + repository: e2e/nri-kube-events + tag: test + pullPolicy: IfNotPresent diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/ci/test-values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/ci/test-values.yaml new file mode 100644 index 0000000000..4e517d6667 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/ci/test-values.yaml @@ -0,0 +1,60 @@ +global: + licenseKey: 1234567890abcdef1234567890abcdef12345678 + cluster: test-cluster + +sinks: + # Enable the stdout sink to also see all events in the logs. + stdout: true + # The newRelicInfra sink sends all events to New relic. + newRelicInfra: true + +customAttributes: + test_tag_label: test_tag_value + +config: + accountID: 111 + region: EU + +rbac: + create: true + +serviceAccount: + create: true + +podAnnotations: + annotation1: "annotation" + +nodeSelector: + kubernetes.io/os: linux + +tolerations: + - key: "key1" + effect: "NoSchedule" + operator: "Exists" + +affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/os + operator: In + values: + - linux + +hostNetwork: true + +dnsConfig: + nameservers: + - 1.2.3.4 + searches: + - my.dns.search.suffix + options: + - name: ndots + value: "1" + +image: + kubeEvents: + repository: e2e/nri-kube-events + tag: test + pullPolicy: IfNotPresent diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/NOTES.txt b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/NOTES.txt new file mode 100644 index 0000000000..3fd06b4a29 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/NOTES.txt @@ -0,0 +1,3 @@ +{{ include "nri-kube-events.compatibility.message.securityContext.runAsUser" . }} + +{{ include "nri-kube-events.compatibility.message.images" . }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/_helpers.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/_helpers.tpl new file mode 100644 index 0000000000..5d0b8d257d --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/_helpers.tpl @@ -0,0 +1,45 @@ +{{/* vim: set filetype=mustache: */}} + +{{- define "nri-kube-events.securityContext.pod" -}} +{{- $defaults := fromYaml ( include "nriKubernetes.securityContext.podDefaults" . ) -}} +{{- $compatibilityLayer := include "nri-kube-events.compatibility.securityContext.pod" . | fromYaml -}} +{{- $commonLibrary := fromYaml ( include "newrelic.common.securityContext.pod" . ) -}} + +{{- $finalSecurityContext := dict -}} +{{- if $commonLibrary -}} + {{- $finalSecurityContext = mustMergeOverwrite $commonLibrary $compatibilityLayer -}} +{{- else -}} + {{- $finalSecurityContext = mustMergeOverwrite $defaults $compatibilityLayer -}} +{{- end -}} +{{- toYaml $finalSecurityContext -}} +{{- end -}} + + + +{{- /* These are the defaults that are used for all the containers in this chart */ -}} +{{- define "nriKubernetes.securityContext.podDefaults" -}} +runAsUser: 1000 +runAsNonRoot: true +{{- end -}} + + + +{{- define "nri-kube-events.securityContext.container" -}} +{{- if include "newrelic.common.securityContext.container" . -}} +{{- include "newrelic.common.securityContext.container" . -}} +{{- else -}} +privileged: false +allowPrivilegeEscalation: false +readOnlyRootFilesystem: true +{{- end -}} +{{- end -}} + + + +{{- /* */ -}} +{{- define "nri-kube-events.agentConfig" -}} +is_forward_only: true +http_server_enabled: true +http_server_port: 8001 +{{ include "newrelic.common.agentConfig.defaults" . }} +{{- end -}} \ No newline at end of file diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/_helpers_compatibility.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/_helpers_compatibility.tpl new file mode 100644 index 0000000000..059cfff12a --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/_helpers_compatibility.tpl @@ -0,0 +1,262 @@ +{{/* +Returns a dictionary with legacy runAsUser config. +We know that it only has "one line" but it is separated from the rest of the helpers because it is a temporary things +that we should EOL. The EOL time of this will be marked when we GA the deprecation of Helm v2. +*/}} +{{- define "nri-kube-events.compatibility.securityContext.pod" -}} +{{- if .Values.runAsUser -}} +runAsUser: {{ .Values.runAsUser }} +{{- end -}} +{{- end -}} + + + +{{- /* +Functions to get values from the globals instead of the common library +We make this because there could be difficult to see what is going under +the hood if we use the common-library here. So it is easy to read something +like: +{{- $registry := $oldRegistry | default $newRegistry | default $globalRegistry -}} +*/ -}} +{{- define "nri-kube-events.compatibility.global.registry" -}} + {{- if .Values.global -}} + {{- if .Values.global.images -}} + {{- if .Values.global.images.registry -}} + {{- .Values.global.images.registry -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- /* Functions to fetch integration image configuration from the old .Values.image */ -}} +{{- /* integration's old registry */ -}} +{{- define "nri-kube-events.compatibility.old.integration.registry" -}} + {{- if .Values.image -}} + {{- if .Values.image.kubeEvents -}} + {{- if .Values.image.kubeEvents.registry -}} + {{- .Values.image.kubeEvents.registry -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- /* integration's old repository */ -}} +{{- define "nri-kube-events.compatibility.old.integration.repository" -}} + {{- if .Values.image -}} + {{- if .Values.image.kubeEvents -}} + {{- if .Values.image.kubeEvents.repository -}} + {{- .Values.image.kubeEvents.repository -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- /* integration's old tag */ -}} +{{- define "nri-kube-events.compatibility.old.integration.tag" -}} + {{- if .Values.image -}} + {{- if .Values.image.kubeEvents -}} + {{- if .Values.image.kubeEvents.tag -}} + {{- .Values.image.kubeEvents.tag -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- /* integration's old imagePullPolicy */ -}} +{{- define "nri-kube-events.compatibility.old.integration.pullPolicy" -}} + {{- if .Values.image -}} + {{- if .Values.image.kubeEvents -}} + {{- if .Values.image.kubeEvents.pullPolicy -}} + {{- .Values.image.kubeEvents.pullPolicy -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- /* Functions to fetch agent image configuration from the old .Values.image */ -}} +{{- /* agent's old registry */ -}} +{{- define "nri-kube-events.compatibility.old.agent.registry" -}} + {{- if .Values.image -}} + {{- if .Values.image.infraAgent -}} + {{- if .Values.image.infraAgent.registry -}} + {{- .Values.image.infraAgent.registry -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- /* agent's old repository */ -}} +{{- define "nri-kube-events.compatibility.old.agent.repository" -}} + {{- if .Values.image -}} + {{- if .Values.image.infraAgent -}} + {{- if .Values.image.infraAgent.repository -}} + {{- .Values.image.infraAgent.repository -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- /* agent's old tag */ -}} +{{- define "nri-kube-events.compatibility.old.agent.tag" -}} + {{- if .Values.image -}} + {{- if .Values.image.infraAgent -}} + {{- if .Values.image.infraAgent.tag -}} + {{- .Values.image.infraAgent.tag -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- /* agent's old imagePullPolicy */ -}} +{{- define "nri-kube-events.compatibility.old.agent.pullPolicy" -}} + {{- if .Values.image -}} + {{- if .Values.image.infraAgent -}} + {{- if .Values.image.infraAgent.pullPolicy -}} + {{- .Values.image.infraAgent.pullPolicy -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} + + + +{{/* +Creates the image string needed to pull the integration image respecting the breaking change we made in the values file +*/}} +{{- define "nri-kube-events.compatibility.images.integration" -}} +{{- $globalRegistry := include "nri-kube-events.compatibility.global.registry" . -}} +{{- $oldRegistry := include "nri-kube-events.compatibility.old.integration.registry" . -}} +{{- $newRegistry := .Values.images.integration.registry -}} +{{- $registry := $oldRegistry | default $newRegistry | default $globalRegistry -}} + +{{- $oldRepository := include "nri-kube-events.compatibility.old.integration.repository" . -}} +{{- $newRepository := .Values.images.integration.repository -}} +{{- $repository := $oldRepository | default $newRepository }} + +{{- $oldTag := include "nri-kube-events.compatibility.old.integration.tag" . -}} +{{- $newTag := .Values.images.integration.tag -}} +{{- $tag := $oldTag | default $newTag | default .Chart.AppVersion -}} + +{{- if $registry -}} + {{- printf "%s/%s:%s" $registry $repository $tag -}} +{{- else -}} + {{- printf "%s:%s" $repository $tag -}} +{{- end -}} +{{- end -}} + + + +{{/* +Creates the image string needed to pull the agent's image respecting the breaking change we made in the values file +*/}} +{{- define "nri-kube-events.compatibility.images.agent" -}} +{{- $globalRegistry := include "nri-kube-events.compatibility.global.registry" . -}} +{{- $oldRegistry := include "nri-kube-events.compatibility.old.agent.registry" . -}} +{{- $newRegistry := .Values.images.agent.registry -}} +{{- $registry := $oldRegistry | default $newRegistry | default $globalRegistry -}} + +{{- $oldRepository := include "nri-kube-events.compatibility.old.agent.repository" . -}} +{{- $newRepository := .Values.images.agent.repository -}} +{{- $repository := $oldRepository | default $newRepository }} + +{{- $oldTag := include "nri-kube-events.compatibility.old.agent.tag" . -}} +{{- $newTag := .Values.images.agent.tag -}} +{{- $tag := $oldTag | default $newTag -}} + +{{- if $registry -}} + {{- printf "%s/%s:%s" $registry $repository $tag -}} +{{- else -}} + {{- printf "%s:%s" $repository $tag -}} +{{- end -}} +{{- end -}} + + + +{{/* +Returns the pull policy for the integration image taking into account that we made a breaking change on the values path. +*/}} +{{- define "nri-kube-events.compatibility.images.pullPolicy.integration" -}} +{{- $old := include "nri-kube-events.compatibility.old.integration.pullPolicy" . -}} +{{- $new := .Values.images.integration.pullPolicy -}} + +{{- $old | default $new -}} +{{- end -}} + + + +{{/* +Returns the pull policy for the agent image taking into account that we made a breaking change on the values path. +*/}} +{{- define "nri-kube-events.compatibility.images.pullPolicy.agent" -}} +{{- $old := include "nri-kube-events.compatibility.old.agent.pullPolicy" . -}} +{{- $new := .Values.images.agent.pullPolicy -}} + +{{- $old | default $new -}} +{{- end -}} + + + +{{/* +Returns a merged list of pull secrets ready to be used +*/}} +{{- define "nri-kube-events.compatibility.images.renderPullSecrets" -}} +{{- $list := list -}} + +{{- if .Values.image -}} + {{- if .Values.image.pullSecrets -}} + {{- $list = append $list .Values.image.pullSecrets }} + {{- end -}} +{{- end -}} + +{{- if .Values.images.pullSecrets -}} + {{- $list = append $list .Values.images.pullSecrets -}} +{{- end -}} + +{{- include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" $list "context" .) }} +{{- end -}} + + + +{{- /* Messege to show to the user saying that image value is not supported anymore */ -}} +{{- define "nri-kube-events.compatibility.message.images" -}} +{{- $oldIntegrationRegistry := include "nri-kube-events.compatibility.old.integration.registry" . -}} +{{- $oldIntegrationRepository := include "nri-kube-events.compatibility.old.integration.repository" . -}} +{{- $oldIntegrationTag := include "nri-kube-events.compatibility.old.integration.tag" . -}} +{{- $oldIntegrationPullPolicy := include "nri-kube-events.compatibility.old.integration.pullPolicy" . -}} +{{- $oldAgentRegistry := include "nri-kube-events.compatibility.old.agent.registry" . -}} +{{- $oldAgentRepository := include "nri-kube-events.compatibility.old.agent.repository" . -}} +{{- $oldAgentTag := include "nri-kube-events.compatibility.old.agent.tag" . -}} +{{- $oldAgentPullPolicy := include "nri-kube-events.compatibility.old.agent.pullPolicy" . -}} + +{{- if or $oldIntegrationRegistry $oldIntegrationRepository $oldIntegrationTag $oldIntegrationPullPolicy $oldAgentRegistry $oldAgentRepository $oldAgentTag $oldAgentPullPolicy }} +Configuring image repository an tag under 'image' is no longer supported. +This is the list values that we no longer support: + - image.kubeEvents.registry + - image.kubeEvents.repository + - image.kubeEvents.tag + - image.kubeEvents.pullPolicy + - image.infraAgent.registry + - image.infraAgent.repository + - image.infraAgent.tag + - image.infraAgent.pullPolicy + +Please set: + - images.agent.* to configure the infrastructure-agent forwarder. + - images.integration.* to configure the image in charge of scraping k8s data. + +------ +{{- end }} +{{- end -}} + + + +{{- /* Messege to show to the user saying that image value is not supported anymore */ -}} +{{- define "nri-kube-events.compatibility.message.securityContext.runAsUser" -}} +{{- if .Values.runAsUser }} +WARNING: `runAsUser` is deprecated +================================== + +We have automatically translated your `runAsUser` setting to the new format, but this shimming will be removed in the +future. Please migrate your configs to the new format in the `securityContext` key. +{{- end }} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/agent-configmap.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/agent-configmap.yaml new file mode 100644 index 0000000000..02bf8306be --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/agent-configmap.yaml @@ -0,0 +1,12 @@ +{{- if .Values.sinks.newRelicInfra -}} +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "newrelic.common.naming.fullname" . }}-agent-config + namespace: {{ .Release.Namespace }} +data: + newrelic-infra.yml: | + {{- include "nri-kube-events.agentConfig" . | nindent 4 }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/clusterrole.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/clusterrole.yaml new file mode 100644 index 0000000000..cbfd5d9cef --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/clusterrole.yaml @@ -0,0 +1,42 @@ +{{- if .Values.rbac.create }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "newrelic.common.naming.fullname" . }} +rules: +- apiGroups: + - "" + resources: + - events + - namespaces + - nodes + - jobs + - persistentvolumes + - persistentvolumeclaims + - pods + - services + verbs: + - get + - watch + - list +- apiGroups: + - apps + resources: + - daemonsets + - deployments + verbs: + - get + - watch + - list +- apiGroups: + - batch + resources: + - cronjobs + - jobs + verbs: + - get + - watch + - list +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/clusterrolebinding.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/clusterrolebinding.yaml new file mode 100644 index 0000000000..fc5dfb8dae --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/clusterrolebinding.yaml @@ -0,0 +1,16 @@ +{{- if .Values.rbac.create }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "newrelic.common.naming.fullname" . }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "newrelic.common.naming.fullname" . }} +subjects: +- kind: ServiceAccount + name: {{ include "newrelic.common.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/configmap.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/configmap.yaml new file mode 100644 index 0000000000..9e4e35f6b3 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/configmap.yaml @@ -0,0 +1,23 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "newrelic.common.naming.fullname" . }}-config + namespace: {{ .Release.Namespace }} +data: + config.yaml: |- + sinks: + {{- if .Values.sinks.stdout }} + - name: stdout + {{- end }} + {{- if .Values.sinks.newRelicInfra }} + - name: newRelicInfra + config: + agentEndpoint: http://localhost:8001/v1/data + clusterName: {{ include "newrelic.common.cluster" . }} + agentHTTPTimeout: {{ .Values.agentHTTPTimeout }} + {{- end }} + captureDescribe: {{ .Values.scrapers.descriptions.enabled }} + describeRefresh: {{ .Values.scrapers.descriptions.resyncPeriod | default "24h" }} + captureEvents: {{ .Values.scrapers.events.enabled }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/deployment.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/deployment.yaml new file mode 100644 index 0000000000..7ba9eaea9c --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/deployment.yaml @@ -0,0 +1,124 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "newrelic.common.naming.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + annotations: + {{- if .Values.deployment.annotations }} + {{- toYaml .Values.deployment.annotations | nindent 4 }} + {{- end }} +spec: + selector: + matchLabels: + app.kubernetes.io/name: {{ include "newrelic.common.naming.name" . }} + template: + metadata: + {{- if .Values.podAnnotations }} + annotations: + {{- toYaml .Values.podAnnotations | nindent 8}} + {{- end }} + labels: + {{- include "newrelic.common.labels.podLabels" . | nindent 8 }} + spec: + {{- with include "nri-kube-events.compatibility.images.renderPullSecrets" . }} + imagePullSecrets: + {{- . | nindent 8 }} + {{- end }} + {{- with include "nri-kube-events.securityContext.pod" . }} + securityContext: + {{- . | nindent 8 }} + {{- end }} + containers: + - name: kube-events + image: {{ include "nri-kube-events.compatibility.images.integration" . }} + imagePullPolicy: {{ include "nri-kube-events.compatibility.images.pullPolicy.integration" . }} + {{- with include "nri-kube-events.securityContext.container" . }} + securityContext: + {{- . | nindent 12 }} + {{- end }} + {{- if .Values.resources }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- end }} + args: ["-config", "/app/config/config.yaml", "-loglevel", "debug"] + volumeMounts: + - name: config-volume + mountPath: /app/config + {{- if .Values.sinks.newRelicInfra }} + - name: forwarder + image: {{ include "nri-kube-events.compatibility.images.agent" . }} + imagePullPolicy: {{ include "nri-kube-events.compatibility.images.pullPolicy.agent" . }} + {{- with include "nri-kube-events.securityContext.container" . }} + securityContext: + {{- . | nindent 12 }} + {{- end }} + ports: + - containerPort: {{ get (fromYaml (include "nri-kube-events.agentConfig" .)) "http_server_port" }} + env: + - name: NRIA_LICENSE_KEY + valueFrom: + secretKeyRef: + name: {{ include "newrelic.common.license.secretName" . }} + key: {{ include "newrelic.common.license.secretKeyName" . }} + + - name: NRIA_OVERRIDE_HOSTNAME_SHORT + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + + volumeMounts: + - mountPath: /var/db/newrelic-infra/data + name: tmpfs-data + - mountPath: /var/db/newrelic-infra/user_data + name: tmpfs-user-data + - mountPath: /tmp + name: tmpfs-tmp + - name: config + mountPath: /etc/newrelic-infra.yml + subPath: newrelic-infra.yml + {{- if ((.Values.forwarder).resources) }} + resources: + {{- toYaml .Values.forwarder.resources | nindent 12 }} + {{- end }} + {{- end }} + serviceAccountName: {{ include "newrelic.common.serviceAccount.name" . }} + volumes: + {{- if .Values.sinks.newRelicInfra }} + - name: config + configMap: + name: {{ include "newrelic.common.naming.fullname" . }}-agent-config + items: + - key: newrelic-infra.yml + path: newrelic-infra.yml + {{- end }} + - name: config-volume + configMap: + name: {{ include "newrelic.common.naming.fullname" . }}-config + - name: tmpfs-data + emptyDir: {} + - name: tmpfs-user-data + emptyDir: {} + - name: tmpfs-tmp + emptyDir: {} + {{- with include "newrelic.common.priorityClassName" . }} + priorityClassName: {{ . }} + {{- end }} + nodeSelector: + kubernetes.io/os: linux + {{ include "newrelic.common.nodeSelector" . | nindent 8 }} + {{- with include "newrelic.common.tolerations" . }} + tolerations: + {{- . | nindent 8 }} + {{- end }} + {{- with include "newrelic.common.affinity" . }} + affinity: + {{- . | nindent 8 }} + {{- end }} + hostNetwork: {{ include "newrelic.common.hostNetwork.value" . }} + {{- with include "newrelic.common.dnsConfig" . }} + dnsConfig: + {{- . | nindent 8 }} + {{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/secret.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/secret.yaml new file mode 100644 index 0000000000..f558ee86c9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/secret.yaml @@ -0,0 +1,2 @@ +{{- /* Common library will take care of creating the secret or not. */}} +{{- include "newrelic.common.license.secret" . }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/serviceaccount.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/serviceaccount.yaml new file mode 100644 index 0000000000..07e818da00 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/templates/serviceaccount.yaml @@ -0,0 +1,11 @@ +{{- if include "newrelic.common.serviceAccount.create" . }} +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "newrelic.common.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} + annotations: +{{ include "newrelic.common.serviceAccount.annotations" . | indent 4 }} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/tests/agent_configmap_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/tests/agent_configmap_test.yaml new file mode 100644 index 0000000000..831b0c5aa1 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/tests/agent_configmap_test.yaml @@ -0,0 +1,46 @@ +suite: test configmap for newrelic infra agent +templates: + - templates/agent-configmap.yaml +release: + name: my-release + namespace: my-namespace +tests: + - it: has the correct default values + set: + cluster: test-cluster + licenseKey: us-whatever + asserts: + - equal: + path: data["newrelic-infra.yml"] + value: | + is_forward_only: true + http_server_enabled: true + http_server_port: 8001 + + - it: integrates properly with the common library + set: + cluster: test-cluster + licenseKey: us-whatever + fedramp.enabled: true + verboseLog: true + asserts: + - equal: + path: data["newrelic-infra.yml"] + value: | + is_forward_only: true + http_server_enabled: true + http_server_port: 8001 + + log: + level: trace + fedramp: true + + - it: does not template if the http sink is disabled + set: + cluster: test-cluster + licenseKey: us-whatever + sinks: + newRelicInfra: false + asserts: + - hasDocuments: + count: 0 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/tests/configmap_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/tests/configmap_test.yaml new file mode 100644 index 0000000000..68ad53a579 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/tests/configmap_test.yaml @@ -0,0 +1,139 @@ +suite: test configmap for sinks +templates: + - templates/configmap.yaml +release: + name: my-release + namespace: my-namespace +tests: + - it: has the correct sinks when default values used + set: + licenseKey: us-whatever + cluster: a-cluster + asserts: + - equal: + path: data["config.yaml"] + value: |- + sinks: + - name: newRelicInfra + config: + agentEndpoint: http://localhost:8001/v1/data + clusterName: a-cluster + agentHTTPTimeout: 30s + captureDescribe: true + describeRefresh: 24h + captureEvents: true + + - it: honors agentHTTPTimeout + set: + licenseKey: us-whatever + cluster: a-cluster + agentHTTPTimeout: 10s + asserts: + - equal: + path: data["config.yaml"] + value: |- + sinks: + - name: newRelicInfra + config: + agentEndpoint: http://localhost:8001/v1/data + clusterName: a-cluster + agentHTTPTimeout: 10s + captureDescribe: true + describeRefresh: 24h + captureEvents: true + + - it: has the correct sinks defined in local values + set: + licenseKey: us-whatever + cluster: a-cluster + sinks: + stdout: true + newRelicInfra: false + asserts: + - equal: + path: data["config.yaml"] + value: |- + sinks: + - name: stdout + captureDescribe: true + describeRefresh: 24h + captureEvents: true + + - it: allows enabling/disabling event scraping + set: + licenseKey: us-whatever + cluster: a-cluster + scrapers: + events: + enabled: false + asserts: + - equal: + path: data["config.yaml"] + value: |- + sinks: + - name: newRelicInfra + config: + agentEndpoint: http://localhost:8001/v1/data + clusterName: a-cluster + agentHTTPTimeout: 30s + captureDescribe: true + describeRefresh: 24h + captureEvents: false + + - it: allows enabling/disabling description scraping + set: + licenseKey: us-whatever + cluster: a-cluster + scrapers: + descriptions: + enabled: false + asserts: + - equal: + path: data["config.yaml"] + value: |- + sinks: + - name: newRelicInfra + config: + agentEndpoint: http://localhost:8001/v1/data + clusterName: a-cluster + agentHTTPTimeout: 30s + captureDescribe: false + describeRefresh: 24h + captureEvents: true + + - it: allows changing description resync intervals + set: + licenseKey: us-whatever + cluster: a-cluster + scrapers: + descriptions: + resyncPeriod: 4h + asserts: + - equal: + path: data["config.yaml"] + value: |- + sinks: + - name: newRelicInfra + config: + agentEndpoint: http://localhost:8001/v1/data + clusterName: a-cluster + agentHTTPTimeout: 30s + captureDescribe: true + describeRefresh: 4h + captureEvents: true + + - it: has another document generated with the proper config set + set: + licenseKey: us-whatever + cluster: a-cluster + sinks: + stdout: false + newRelicInfra: false + asserts: + - equal: + path: data["config.yaml"] + value: |- + sinks: + captureDescribe: true + describeRefresh: 24h + captureEvents: true diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/tests/deployment_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/tests/deployment_test.yaml new file mode 100644 index 0000000000..702917bcea --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/tests/deployment_test.yaml @@ -0,0 +1,104 @@ +suite: test deployment images +templates: + - templates/deployment.yaml +release: + name: my-release + namespace: my-namespace +tests: + - it: deployment image uses pullSecrets + set: + cluster: my-cluster + licenseKey: us-whatever + images: + pullSecrets: + - name: regsecret + asserts: + - equal: + path: spec.template.spec.imagePullSecrets + value: + - name: regsecret + + - it: deployment images use the proper image tag + set: + cluster: test-cluster + licenseKey: us-whatever + images: + integration: + repository: newrelic/nri-kube-events + tag: "latest" + agent: + repository: newrelic/k8s-events-forwarder + tag: "latest" + asserts: + - matchRegex: + path: spec.template.spec.containers[0].image + pattern: .*newrelic/nri-kube-events:latest$ + - matchRegex: + path: spec.template.spec.containers[1].image + pattern: .*newrelic/k8s-events-forwarder:latest$ + + + - it: by default the agent forwarder templates + set: + cluster: test-cluster + licenseKey: us-whatever + asserts: + - contains: + path: spec.template.spec.containers + any: true + content: + name: forwarder + - contains: + path: spec.template.spec.volumes + content: + name: config + configMap: + name: my-release-nri-kube-events-agent-config + items: + - key: newrelic-infra.yml + path: newrelic-infra.yml + + - it: agent does not template if the sink is disabled + set: + cluster: test-cluster + licenseKey: us-whatever + sinks: + newRelicInfra: false + asserts: + - notContains: + path: spec.template.spec.containers + any: true + content: + name: forwarder + - notContains: + path: spec.template.spec.volumes + content: + name: config + configMap: + name: my-release-nri-kube-events-agent-config + items: + - key: newrelic-infra.yml + path: newrelic-infra.yml + + - it: has a linux node selector by default + set: + cluster: my-cluster + licenseKey: us-whatever + asserts: + - equal: + path: spec.template.spec.nodeSelector + value: + kubernetes.io/os: linux + + - it: has a linux node selector and additional selectors + set: + cluster: my-cluster + licenseKey: us-whatever + nodeSelector: + aCoolTestLabel: aCoolTestValue + asserts: + - equal: + path: spec.template.spec.nodeSelector + value: + kubernetes.io/os: linux + aCoolTestLabel: aCoolTestValue diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/tests/images_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/tests/images_test.yaml new file mode 100644 index 0000000000..361be582b5 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/tests/images_test.yaml @@ -0,0 +1,168 @@ +suite: test image compatibility layer +templates: + - templates/deployment.yaml +release: + name: my-release + namespace: my-namespace +tests: + - it: by default the tag is not nil + set: + cluster: test-cluster + licenseKey: us-whatever + asserts: + - notMatchRegex: + path: spec.template.spec.containers[0].image + pattern: ".*nil.*" + - notMatchRegex: + path: spec.template.spec.containers[1].image + pattern: ".*nil.*" + + - it: templates image correctly from the new values + set: + cluster: test-cluster + licenseKey: us-whatever + images: + integration: + registry: ireg + repository: irep + tag: itag + agent: + registry: areg + repository: arep + tag: atag + asserts: + - equal: + path: spec.template.spec.containers[0].image + value: ireg/irep:itag + - equal: + path: spec.template.spec.containers[1].image + value: areg/arep:atag + + - it: templates image correctly from old values + set: + cluster: test-cluster + licenseKey: us-whatever + image: + kubeEvents: + registry: ireg + repository: irep + tag: itag + infraAgent: + registry: areg + repository: arep + tag: atag + asserts: + - equal: + path: spec.template.spec.containers[0].image + value: ireg/irep:itag + - equal: + path: spec.template.spec.containers[1].image + value: areg/arep:atag + + - it: old image values take precedence + set: + cluster: test-cluster + licenseKey: us-whatever + images: + integration: + registry: inew + repository: inew + tag: inew + agent: + registry: anew + repository: anew + tag: anew + image: + kubeEvents: + registry: iold + repository: iold + tag: iold + infraAgent: + registry: aold + repository: aold + tag: aold + asserts: + - equal: + path: spec.template.spec.containers[0].image + value: iold/iold:iold + - equal: + path: spec.template.spec.containers[1].image + value: aold/aold:aold + + - it: pullImagePolicy templates correctly from the new values + set: + cluster: test-cluster + licenseKey: us-whatever + images: + integration: + pullPolicy: new + agent: + pullPolicy: new + asserts: + - equal: + path: spec.template.spec.containers[0].imagePullPolicy + value: new + - equal: + path: spec.template.spec.containers[1].imagePullPolicy + value: new + + - it: pullImagePolicy templates correctly from old values + set: + cluster: test-cluster + licenseKey: us-whatever + image: + kubeEvents: + pullPolicy: old + infraAgent: + pullPolicy: old + asserts: + - equal: + path: spec.template.spec.containers[0].imagePullPolicy + value: old + - equal: + path: spec.template.spec.containers[1].imagePullPolicy + value: old + + - it: old imagePullPolicy values take precedence + set: + cluster: test-cluster + licenseKey: us-whatever + images: + integration: + pullPolicy: new + agent: + pullPolicy: new + image: + kubeEvents: + pullPolicy: old + infraAgent: + pullPolicy: old + asserts: + - equal: + path: spec.template.spec.containers[0].imagePullPolicy + value: old + - equal: + path: spec.template.spec.containers[1].imagePullPolicy + value: old + + - it: imagePullSecrets merge properly + set: + cluster: test-cluster + licenseKey: us-whatever + global: + images: + pullSecrets: + - global: global + images: + pullSecrets: + - images: images + image: + pullSecrets: + - image: image + asserts: + - equal: + path: spec.template.spec.imagePullSecrets + value: + - global: global + - image: image + - images: images diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/tests/security_context_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/tests/security_context_test.yaml new file mode 100644 index 0000000000..b2b710331f --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/tests/security_context_test.yaml @@ -0,0 +1,77 @@ +suite: test deployment security context +templates: + - templates/deployment.yaml +release: + name: my-release + namespace: my-namespace +tests: + - it: pod securityContext set to defaults when no values provided + set: + cluster: my-cluster + licenseKey: us-whatever + asserts: + - equal: + path: spec.template.spec.securityContext + value: + runAsUser: 1000 + runAsNonRoot: true + - it: pod securityContext set common-library values + set: + cluster: test-cluster + licenseKey: us-whatever + podSecurityContext: + foobar: true + asserts: + - equal: + path: spec.template.spec.securityContext.foobar + value: true + - it: pod securityContext compatibility layer overrides values from common-library + set: + cluster: test-cluster + licenseKey: us-whatever + runAsUser: 1001 + podSecurityContext: + runAsUser: 1000 + runAsNonRoot: false + asserts: + - equal: + path: spec.template.spec.securityContext + value: + runAsUser: 1001 + runAsNonRoot: false + - it: pod securityContext compatibility layer overrides defaults + set: + cluster: test-cluster + licenseKey: us-whatever + runAsUser: 1001 + asserts: + - equal: + path: spec.template.spec.securityContext.runAsUser + value: 1001 + - it: set to defaults when no containerSecurityContext set + set: + cluster: my-cluster + licenseKey: us-whatever + asserts: + - equal: + path: spec.template.spec.containers[0].securityContext + value: + allowPrivilegeEscalation: false + privileged: false + readOnlyRootFilesystem: true + - equal: + path: spec.template.spec.containers[1].securityContext + value: + allowPrivilegeEscalation: false + privileged: false + readOnlyRootFilesystem: true + - it: set containerSecurityContext custom values + set: + cluster: test-cluster + licenseKey: us-whatever + containerSecurityContext: + foobar: true + asserts: + - equal: + path: spec.template.spec.containers[0].securityContext.foobar + value: true diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/values.yaml new file mode 100644 index 0000000000..99fe4761be --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-kube-events/values.yaml @@ -0,0 +1,135 @@ +# -- Override the name of the chart +nameOverride: "" +# -- Override the full name of the release +fullnameOverride: "" + +# -- Name of the Kubernetes cluster monitored. Mandatory. Can be configured also with `global.cluster` +cluster: "" +# -- This set this license key to use. Can be configured also with `global.licenseKey` +licenseKey: "" +# -- In case you don't want to have the license key in you values, this allows you to point to a user created secret to get the key from there. Can be configured also with `global.customSecretName` +customSecretName: "" +# -- In case you don't want to have the license key in you values, this allows you to point to which secret key is the license key located. Can be configured also with `global.customSecretLicenseKey` +customSecretLicenseKey: "" + +# -- Images used by the chart for the integration and agents +# @default -- See `values.yaml` +images: + # -- Image for the New Relic Kubernetes integration + # @default -- See `values.yaml` + integration: + registry: + repository: newrelic/nri-kube-events + tag: + pullPolicy: IfNotPresent + # -- Image for the New Relic Infrastructure Agent sidecar + # @default -- See `values.yaml` + agent: + registry: + repository: newrelic/k8s-events-forwarder + tag: 1.56.1 + pullPolicy: IfNotPresent + # -- The secrets that are needed to pull images from a custom registry. + pullSecrets: [] + # - name: regsecret + +# -- Resources for the integration container +resources: {} + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +# -- Resources for the forwarder sidecar container +forwarder: + resources: {} + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +rbac: + # -- Specifies whether RBAC resources should be created + create: true + +# -- Settings controlling ServiceAccount creation +# @default -- See `values.yaml` +serviceAccount: + # serviceAccount.create -- (bool) Specifies whether a ServiceAccount should be created + # @default -- `true` + create: + # If not set and create is true, a name is generated using the fullname template + name: "" + # Specify any annotations to add to the ServiceAccount + annotations: + +# -- Annotations to add to the pod. +podAnnotations: {} +deployment: + # deployment.annotations -- Annotations to add to the Deployment. + annotations: {} +# -- Additional labels for chart pods +podLabels: {} +# -- Additional labels for chart objects +labels: {} + +# -- Amount of time to wait until timeout to send metrics to the metric forwarder +agentHTTPTimeout: "30s" + +# -- Configure where will the metrics be written. Mostly for debugging purposes. +# @default -- See `values.yaml` +sinks: + # -- Enable the stdout sink to also see all events in the logs. + stdout: false + # -- The newRelicInfra sink sends all events to New Relic. + newRelicInfra: true + +# -- Configure the various kinds of scrapers that should be run. +# @default -- See `values.yaml` +scrapers: + descriptions: + enabled: true + resyncPeriod: "24h" + events: + enabled: true + +# -- Sets pod's priorityClassName. Can be configured also with `global.priorityClassName` +priorityClassName: "" +# -- (bool) Sets pod's hostNetwork. Can be configured also with `global.hostNetwork` +# @default -- `false` +hostNetwork: +# -- Sets pod's dnsConfig. Can be configured also with `global.dnsConfig` +dnsConfig: {} +# -- Sets security context (at pod level). Can be configured also with `global.podSecurityContext` +podSecurityContext: {} +# -- Sets security context (at container level). Can be configured also with `global.containerSecurityContext` +containerSecurityContext: {} + +# -- Sets pod/node affinities. Can be configured also with `global.affinity` +affinity: {} +# -- Sets pod's node selector. Can be configured also with `global.nodeSelector` +nodeSelector: {} +# -- Sets pod's tolerations to node taints. Can be configured also with `global.tolerations` +tolerations: [] + +# -- Adds extra attributes to the cluster and all the metrics emitted to the backend. Can be configured also with `global.customAttributes` +customAttributes: {} + +# -- Configures the integration to send all HTTP/HTTPS request through the proxy in that URL. The URL should have a standard format like `https://user:password@hostname:port`. Can be configured also with `global.proxy` +proxy: "" + +# -- (bool) Send the metrics to the staging backend. Requires a valid staging license key. Can be configured also with `global.nrStaging` +# @default -- `false` +nrStaging: +fedramp: + # -- (bool) Enables FedRAMP. Can be configured also with `global.fedramp.enabled` + # @default -- `false` + enabled: + +# -- (bool) Sets the debug logs to this integration or all integrations if it is set globally. Can be configured also with `global.verboseLog` +# @default -- `false` +verboseLog: diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/.helmignore b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/.helmignore new file mode 100644 index 0000000000..f62b5519e5 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/.helmignore @@ -0,0 +1 @@ +templates/admission-webhooks/job-patch/README.md diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/Chart.lock b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/Chart.lock new file mode 100644 index 0000000000..d442841bfa --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: common-library + repository: https://helm-charts.newrelic.com + version: 1.3.0 +digest: sha256:2e1da613fd8a52706bde45af077779c5d69e9e1641bdf5c982eaf6d1ac67a443 +generated: "2024-08-30T00:58:04.140696675Z" diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/Chart.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/Chart.yaml new file mode 100644 index 0000000000..afc70d416c --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/Chart.yaml @@ -0,0 +1,25 @@ +apiVersion: v2 +appVersion: 1.29.0 +dependencies: +- name: common-library + repository: https://helm-charts.newrelic.com + version: 1.3.0 +description: A Helm chart to deploy the New Relic metadata injection webhook. +home: https://hub.docker.com/r/newrelic/k8s-metadata-injection +icon: https://newrelic.com/assets/newrelic/source/NewRelic-logo-square.svg +keywords: +- infrastructure +- newrelic +- monitoring +maintainers: +- name: juanjjaramillo + url: https://github.com/juanjjaramillo +- name: csongnr + url: https://github.com/csongnr +- name: dbudziwojskiNR + url: https://github.com/dbudziwojskiNR +name: nri-metadata-injection +sources: +- https://github.com/newrelic/k8s-metadata-injection +- https://github.com/newrelic/k8s-metadata-injection/tree/master/charts/nri-metadata-injection +version: 4.21.0 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/README.md b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/README.md new file mode 100644 index 0000000000..dd922ef13a --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/README.md @@ -0,0 +1,68 @@ +# nri-metadata-injection + +A Helm chart to deploy the New Relic metadata injection webhook. + +**Homepage:** + +# Helm installation + +You can install this chart using [`nri-bundle`](https://github.com/newrelic/helm-charts/tree/master/charts/nri-bundle) located in the +[helm-charts repository](https://github.com/newrelic/helm-charts) or directly from this repository by adding this Helm repository: + +```shell +helm repo add nri-metadata-injection https://newrelic.github.io/k8s-metadata-injection +helm upgrade --install nri-metadata-injection/nri-metadata-injection -f your-custom-values.yaml +``` + +## Source Code + +* +* + +## Values managed globally + +This chart implements the [New Relic's common Helm library](https://github.com/newrelic/helm-charts/tree/master/library/common-library) which +means that it honors a wide range of defaults and globals common to most New Relic Helm charts. + +Options that can be defined globally include `affinity`, `nodeSelector`, `tolerations`, `proxy` and others. The full list can be found at +[user's guide of the common library](https://github.com/newrelic/helm-charts/blob/master/library/common-library/README.md). + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| affinity | object | `{}` | Sets pod/node affinities. Can be configured also with `global.affinity` | +| certManager.enabled | bool | `false` | Use cert manager for webhook certs | +| certManager.rootCertificateDuration | string | `"43800h"` | Sets the root certificate duration. Defaults to 43800h (5 years). | +| certManager.webhookCertificateDuration | string | `"8760h"` | Sets certificate duration. Defaults to 8760h (1 year). | +| cluster | string | `""` | Name of the Kubernetes cluster monitored. Can be configured also with `global.cluster` | +| containerSecurityContext | object | `{}` | Sets security context (at container level). Can be configured also with `global.containerSecurityContext` | +| customTLSCertificate | bool | `false` | Use custom tls certificates for the webhook, or let the chart handle it automatically. Ref: https://docs.newrelic.com/docs/integrations/kubernetes-integration/link-your-applications/link-your-applications-kubernetes#configure-injection | +| dnsConfig | object | `{}` | Sets pod's dnsConfig. Can be configured also with `global.dnsConfig` | +| fullnameOverride | string | `""` | Override the full name of the release | +| hostNetwork | bool | false | Sets pod's hostNetwork. Can be configured also with `global.hostNetwork` | +| image | object | See `values.yaml` | Image for the New Relic Metadata Injector | +| image.pullSecrets | list | `[]` | The secrets that are needed to pull images from a custom registry. | +| injectOnlyLabeledNamespaces | bool | `false` | Enable the metadata decoration only for pods living in namespaces labeled with 'newrelic-metadata-injection=enabled'. | +| jobImage | object | See `values.yaml` | Image for creating the needed certificates of this webhook to work | +| jobImage.pullSecrets | list | `[]` | The secrets that are needed to pull images from a custom registry. | +| jobImage.volumeMounts | list | `[]` | Volume mounts to add to the job, you might want to mount tmp if Pod Security Policies Enforce a read-only root. | +| jobImage.volumes | list | `[]` | Volumes to add to the job container | +| labels | object | `{}` | Additional labels for chart objects. Can be configured also with `global.labels` | +| nameOverride | string | `""` | Override the name of the chart | +| nodeSelector | object | `{}` | Sets pod's node selector. Can be configured also with `global.nodeSelector` | +| podAnnotations | object | `{}` | Annotations to be added to all pods created by the integration. | +| podLabels | object | `{}` | Additional labels for chart pods. Can be configured also with `global.podLabels` | +| podSecurityContext | object | `{}` | Sets security context (at pod level). Can be configured also with `global.podSecurityContext` | +| priorityClassName | string | `""` | Sets pod's priorityClassName. Can be configured also with `global.priorityClassName` | +| rbac.pspEnabled | bool | `false` | Whether the chart should create Pod Security Policy objects. | +| replicas | int | `1` | | +| resources | object | 100m/30M -/80M | Image for creating the needed certificates of this webhook to work | +| timeoutSeconds | int | `28` | Webhook timeout Ref: https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#timeouts | +| tolerations | list | `[]` | Sets pod's tolerations to node taints. Can be configured also with `global.tolerations` | + +## Maintainers + +* [juanjjaramillo](https://github.com/juanjjaramillo) +* [csongnr](https://github.com/csongnr) +* [dbudziwojskiNR](https://github.com/dbudziwojskiNR) diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/README.md.gotmpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/README.md.gotmpl new file mode 100644 index 0000000000..752ba8aae8 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/README.md.gotmpl @@ -0,0 +1,41 @@ +{{ template "chart.header" . }} +{{ template "chart.deprecationWarning" . }} + +{{ template "chart.description" . }} + +{{ template "chart.homepageLine" . }} + +# Helm installation + +You can install this chart using [`nri-bundle`](https://github.com/newrelic/helm-charts/tree/master/charts/nri-bundle) located in the +[helm-charts repository](https://github.com/newrelic/helm-charts) or directly from this repository by adding this Helm repository: + +```shell +helm repo add nri-metadata-injection https://newrelic.github.io/k8s-metadata-injection +helm upgrade --install nri-metadata-injection/nri-metadata-injection -f your-custom-values.yaml +``` + +{{ template "chart.sourcesSection" . }} + +## Values managed globally + +This chart implements the [New Relic's common Helm library](https://github.com/newrelic/helm-charts/tree/master/library/common-library) which +means that it honors a wide range of defaults and globals common to most New Relic Helm charts. + +Options that can be defined globally include `affinity`, `nodeSelector`, `tolerations`, `proxy` and others. The full list can be found at +[user's guide of the common library](https://github.com/newrelic/helm-charts/blob/master/library/common-library/README.md). + +{{ template "chart.valuesSection" . }} + +{{ if .Maintainers }} +## Maintainers +{{ range .Maintainers }} +{{- if .Name }} +{{- if .Url }} +* [{{ .Name }}]({{ .Url }}) +{{- else }} +* {{ .Name }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/.helmignore b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/.helmignore new file mode 100644 index 0000000000..0e8a0eb36f --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/.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 +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/Chart.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/Chart.yaml new file mode 100644 index 0000000000..f2ee5497ee --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/Chart.yaml @@ -0,0 +1,17 @@ +apiVersion: v2 +description: Provides helpers to provide consistency on all the charts +keywords: +- newrelic +- chart-library +maintainers: +- name: juanjjaramillo + url: https://github.com/juanjjaramillo +- name: csongnr + url: https://github.com/csongnr +- name: dbudziwojskiNR + url: https://github.com/dbudziwojskiNR +- name: kang-makes + url: https://github.com/kang-makes +name: common-library +type: library +version: 1.3.0 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/DEVELOPERS.md b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/DEVELOPERS.md new file mode 100644 index 0000000000..7208c673ec --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/DEVELOPERS.md @@ -0,0 +1,747 @@ +# Functions/templates documented for chart writers +Here is some rough documentation separated by the file that contains the function, the function +name and how to use it. We are not covering functions that start with `_` (e.g. +`newrelic.common.license._licenseKey`) because they are used internally by this library for +other helpers. Helm does not have the concept of "public" or "private" functions/templates so +this is a convention of ours. + +## _naming.tpl +These functions are used to name objects. + +### `newrelic.common.naming.name` +This is the same as the idiomatic `CHART-NAME.name` that is created when you use `helm create`. + +It honors `.Values.nameOverride`. + +Usage: +```mustache +{{ include "newrelic.common.naming.name" . }} +``` + +### `newrelic.common.naming.fullname` +This is the same as the idiomatic `CHART-NAME.fullname` that is created when you use `helm create` + +It honors `.Values.fullnameOverride`. + +Usage: +```mustache +{{ include "newrelic.common.naming.fullname" . }} +``` + +### `newrelic.common.naming.chart` +This is the same as the idiomatic `CHART-NAME.chart` that is created when you use `helm create`. + +It is mostly useless for chart writers. It is used internally for templating the labels but there +is no reason to keep it "private". + +Usage: +```mustache +{{ include "newrelic.common.naming.chart" . }} +``` + +### `newrelic.common.naming.truncateToDNS` +This is a useful template that could be used to trim a string to 63 chars and does not end with a dash (`-`). +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). + +Usage: +```mustache +{{ $nameToTruncate := "a-really-really-really-really-REALLY-long-string-that-should-be-truncated-because-it-is-enought-long-to-brak-something" +{{- $truncatedName := include "newrelic.common.naming.truncateToDNS" $nameToTruncate }} +{{- $truncatedName }} +{{- /* This should print: a-really-really-really-really-REALLY-long-string-that-should-be */ -}} +``` + +### `newrelic.common.naming.truncateToDNSWithSuffix` +This template function is the same as the above but instead of receiving a string you should give a `dict` +with a `name` and a `suffix`. This function will join them with a dash (`-`) and trim the `name` so the +result of `name-suffix` is no more than 63 chars + +Usage: +```mustache +{{ $nameToTruncate := "a-really-really-really-really-REALLY-long-string-that-should-be-truncated-because-it-is-enought-long-to-brak-something" +{{- $suffix := "A-NOT-SO-LONG-SUFFIX" }} +{{- $truncatedName := include "truncateToDNSWithSuffix" (dict "name" $nameToTruncate "suffix" $suffix) }} +{{- $truncatedName }} +{{- /* This should print: a-really-really-really-really-REALLY-long-A-NOT-SO-LONG-SUFFIX */ -}} +``` + + + +## _labels.tpl +### `newrelic.common.labels`, `newrelic.common.labels.selectorLabels` and `newrelic.common.labels.podLabels` +These are functions that are used to label objects. They are configured by this `values.yaml` +```yaml +global: + podLabels: {} # included in all the pods of all the charts that implement this library + labels: {} # included in all the objects of all the charts that implement this library +podLabels: {} # included in all the pods of this chart +labels: {} # included in all the objects of this chart +``` + +label maps are merged from global to local values. + +And chart writer should use them like this: +```mustache +metadata: + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +spec: + selector: + matchLabels: + {{- include "newrelic.common.labels.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "newrelic.common.labels.podLabels" . | nindent 8 }} +``` + +`newrelic.common.labels.podLabels` includes `newrelic.common.labels.selectorLabels` automatically. + + + +## _priority-class-name.tpl +### `newrelic.common.priorityClassName` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + priorityClassName: "" +priorityClassName: "" +``` + +Be careful: chart writers should put an empty string (or any kind of Helm falsiness) for this +library to work properly. If in your values a non-falsy `priorityClassName` is found, the global +one is going to be always ignored. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.priorityClassName" . }} + priorityClassName: {{ . }} + {{- end }} +``` + + + +## _hostnetwork.tpl +### `newrelic.common.hostNetwork` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + hostNetwork: # Note that this is empty (nil) +hostNetwork: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `hostNetwork` is defined, the global one is going to be always ignored. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.hostNetwork" . }} + hostNetwork: {{ . }} + {{- end }} +``` + +### `newrelic.common.hostNetwork.value` +This function is an abstraction of the function above but this returns directly "true" or "false". + +Be careful with using this with an `if` as Helm does evaluate "false" (string) as `true`. + +Usage (example in a pod spec): +```mustache +spec: + hostNetwork: {{ include "newrelic.common.hostNetwork.value" . }} +``` + + + +## _dnsconfig.tpl +### `newrelic.common.dnsConfig` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + dnsConfig: {} +dnsConfig: {} +``` + +Be careful: chart writers should put an empty string (or any kind of Helm falsiness) for this +library to work properly. If in your values a non-falsy `dnsConfig` is found, the global +one is going to be always ignored. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.dnsConfig" . }} + dnsConfig: + {{- . | nindent 4 }} + {{- end }} +``` + + + +## _images.tpl +These functions help us to deal with how images are templated. This allows setting `registries` +where to fetch images globally while being flexible enough to fit in different maps of images +and deployments with one or more images. This is the example of a complex `values.yaml` that +we are going to use during the documentation of these functions: + +```yaml +global: + images: + registry: nexus-3-instance.internal.clients-domain.tld +jobImage: + registry: # defaults to "example.tld" when empty in these examples + repository: ingress-nginx/kube-webhook-certgen + tag: v1.1.1 + pullPolicy: IfNotPresent + pullSecrets: [] +images: + integration: + registry: + repository: newrelic/nri-kube-events + tag: 1.8.0 + pullPolicy: IfNotPresent + agent: + registry: + repository: newrelic/k8s-events-forwarder + tag: 1.22.0 + pullPolicy: IfNotPresent + pullSecrets: [] +``` + +### `newrelic.common.images.image` +This will return a string with the image ready to be downloaded that includes the registry, the image and the tag. +`defaultRegistry` is used to keep `registry` field empty in `values.yaml` so you can override the image using +`global.images.registry`, your local `jobImage.registry` and be able to fallback to a registry that is not `docker.io` +(Or the default repository that the client could have set in the CRI). + +Usage: +```mustache +{{- /* For the integration */}} +{{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.images.agent "context" .) }} +{{- /* For jobImage */}} +{{ include "newrelic.common.images.image" ( dict "defaultRegistry" "example.tld" "imageRoot" .Values.jobImage "context" .) }} +``` + +### `newrelic.common.images.registry` +It returns the registry from the global or local values. You should avoid using this helper to create your image +URL and use `newrelic.common.images.image` instead, but it is there to be used in case it is needed. + +Usage: +```mustache +{{- /* For the integration */}} +{{ include "newrelic.common.images.registry" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.registry" ( dict "imageRoot" .Values.images.agent "context" .) }} +{{- /* For jobImage */}} +{{ include "newrelic.common.images.registry" ( dict "defaultRegistry" "example.tld" "imageRoot" .Values.jobImage "context" .) }} +``` + +### `newrelic.common.images.repository` +It returns the image from the values. You should avoid using this helper to create your image +URL and use `newrelic.common.images.image` instead, but it is there to be used in case it is needed. + +Usage: +```mustache +{{- /* For jobImage */}} +{{ include "newrelic.common.images.repository" ( dict "imageRoot" .Values.jobImage "context" .) }} +{{- /* For the integration */}} +{{ include "newrelic.common.images.repository" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.repository" ( dict "imageRoot" .Values.images.agent "context" .) }} +``` + +### `newrelic.common.images.tag` +It returns the image's tag from the values. You should avoid using this helper to create your image +URL and use `newrelic.common.images.image` instead, but it is there to be used in case it is needed. + +Usage: +```mustache +{{- /* For jobImage */}} +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.jobImage "context" .) }} +{{- /* For the integration */}} +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.images.agent "context" .) }} +``` + +### `newrelic.common.images.renderPullSecrets` +If returns a merged map that contains the pull secrets from the global configuration and the local one. + +Usage: +```mustache +{{- /* For jobImage */}} +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" .Values.jobImage.pullSecrets "context" .) }} +{{- /* For the integration */}} +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" .Values.images.pullSecrets "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" .Values.images.pullSecrets "context" .) }} +``` + + + +## _serviceaccount.tpl +These functions are used to evaluate if the service account should be created, with which name and add annotations to it. + +The functions that the common library has implemented for service accounts are: +* `newrelic.common.serviceAccount.create` +* `newrelic.common.serviceAccount.name` +* `newrelic.common.serviceAccount.annotations` + +Usage: +```mustache +{{- if include "newrelic.common.serviceAccount.create" . -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + {{- with (include "newrelic.common.serviceAccount.annotations" .) }} + annotations: + {{- . | nindent 4 }} + {{- end }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "newrelic.common.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} +{{- end }} +``` + + + +## _affinity.tpl, _nodeselector.tpl and _tolerations.tpl +These three files are almost the same and they follow the idiomatic way of `helm create`. + +Each function also looks if there is a global value like the other helpers. +```yaml +global: + affinity: {} + nodeSelector: {} + tolerations: [] +affinity: {} +nodeSelector: {} +tolerations: [] +``` + +The values here are replaced instead of be merged. If a value at root level is found, the global one is ignored. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.nodeSelector" . }} + nodeSelector: + {{- . | nindent 4 }} + {{- end }} + {{- with include "newrelic.common.affinity" . }} + affinity: + {{- . | nindent 4 }} + {{- end }} + {{- with include "newrelic.common.tolerations" . }} + tolerations: + {{- . | nindent 4 }} + {{- end }} +``` + + + +## _agent-config.tpl +### `newrelic.common.agentConfig.defaults` +This returns a YAML that the agent can use directly as a config that includes other options from the values file like verbose mode, +custom attributes, FedRAMP and such. + +Usage: +```mustache +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include newrelic.common.naming.truncateToDNSWithSuffix (dict "name" (include "newrelic.common.naming.fullname" .) suffix "agent-config") }} + namespace: {{ .Release.Namespace }} +data: + newrelic-infra.yml: |- + # This is the configuration file for the infrastructure agent. See: + # https://docs.newrelic.com/docs/infrastructure/install-infrastructure-agent/configuration/infrastructure-agent-configuration-settings/ + {{- include "newrelic.common.agentConfig.defaults" . | nindent 4 }} +``` + + + +## _cluster.tpl +### `newrelic.common.cluster` +Returns the cluster name + +Usage: +```mustache +{{ include "newrelic.common.cluster" . }} +``` + + + +## _custom-attributes.tpl +### `newrelic.common.customAttributes` +Return custom attributes in YAML format. + +Usage: +```mustache +apiVersion: v1 +kind: ConfigMap +metadata: + name: example +data: + custom-attributes.yaml: | + {{- include "newrelic.common.customAttributes" . | nindent 4 }} + custom-attributes.json: | + {{- include "newrelic.common.customAttributes" . | fromYaml | toJson | nindent 4 }} +``` + + + +## _fedramp.tpl +### `newrelic.common.fedramp.enabled` +Returns true if FedRAMP is enabled or an empty string if not. It can be safely used in conditionals as an empty string is a Helm falsiness. + +Usage: +```mustache +{{ include "newrelic.common.fedramp.enabled" . }} +``` + +### `newrelic.common.fedramp.enabled.value` +Returns true if FedRAMP is enabled or false if not. This is to have the value of FedRAMP ready to be templated. + +Usage: +```mustache +{{ include "newrelic.common.fedramp.enabled.value" . }} +``` + + + +## _license.tpl +### `newrelic.common.license.secretName` and ### `newrelic.common.license.secretKeyName` +Returns the secret and key inside the secret where to read the license key. + +The common library will take care of using a user-provided custom secret or creating a secret that contains the license key. + +To create the secret use `newrelic.common.license.secret`. + +Usage: +```mustache +{{- if and (.Values.controlPlane.enabled) (not (include "newrelic.fargate" .)) }} +apiVersion: v1 +kind: Pod +metadata: + name: example +spec: + containers: + - name: agent + env: + - name: "NRIA_LICENSE_KEY" + valueFrom: + secretKeyRef: + name: {{ include "newrelic.common.license.secretName" . }} + key: {{ include "newrelic.common.license.secretKeyName" . }} +``` + + + +## _license_secret.tpl +### `newrelic.common.license.secret` +This function templates the secret that is used by agents and integrations with the license Key provided by the user. It will +template nothing (empty string) if the user provides a custom pair of secret name and key. + +This template also fails in case the user has not provided any license key or custom secret so no safety checks have to be done +by chart writers. + +You just must have a template with these two lines: +```mustache +{{- /* Common library will take care of creating the secret or not. */ -}} +{{- include "newrelic.common.license.secret" . -}} +``` + + + +## _insights.tpl +### `newrelic.common.insightsKey.secretName` and ### `newrelic.common.insightsKey.secretKeyName` +Returns the secret and key inside the secret where to read the insights key. + +The common library will take care of using a user-provided custom secret or creating a secret that contains the insights key. + +To create the secret use `newrelic.common.insightsKey.secret`. + +Usage: +```mustache +apiVersion: v1 +kind: Pod +metadata: + name: statsd +spec: + containers: + - name: statsd + env: + - name: "INSIGHTS_KEY" + valueFrom: + secretKeyRef: + name: {{ include "newrelic.common.insightsKey.secretName" . }} + key: {{ include "newrelic.common.insightsKey.secretKeyName" . }} +``` + + + +## _insights_secret.tpl +### `newrelic.common.insightsKey.secret` +This function templates the secret that is used by agents and integrations with the insights key provided by the user. It will +template nothing (empty string) if the user provides a custom pair of secret name and key. + +This template also fails in case the user has not provided any insights key or custom secret so no safety checks have to be done +by chart writers. + +You just must have a template with these two lines: +```mustache +{{- /* Common library will take care of creating the secret or not. */ -}} +{{- include "newrelic.common.insightsKey.secret" . -}} +``` + + + +## _userkey.tpl +### `newrelic.common.userKey.secretName` and ### `newrelic.common.userKey.secretKeyName` +Returns the secret and key inside the secret where to read a user key. + +The common library will take care of using a user-provided custom secret or creating a secret that contains the insights key. + +To create the secret use `newrelic.common.userKey.secret`. + +Usage: +```mustache +apiVersion: v1 +kind: Pod +metadata: + name: statsd +spec: + containers: + - name: statsd + env: + - name: "API_KEY" + valueFrom: + secretKeyRef: + name: {{ include "newrelic.common.userKey.secretName" . }} + key: {{ include "newrelic.common.userKey.secretKeyName" . }} +``` + + + +## _userkey_secret.tpl +### `newrelic.common.userKey.secret` +This function templates the secret that is used by agents and integrations with a user key provided by the user. It will +template nothing (empty string) if the user provides a custom pair of secret name and key. + +This template also fails in case the user has not provided any API key or custom secret so no safety checks have to be done +by chart writers. + +You just must have a template with these two lines: +```mustache +{{- /* Common library will take care of creating the secret or not. */ -}} +{{- include "newrelic.common.userKey.secret" . -}} +``` + + + +## _region.tpl +### `newrelic.common.region.validate` +Given a string, return a normalized name for the region if valid. + +This function does not need the context of the chart, only the value to be validated. The region returned +honors the region [definition of the newrelic-client-go implementation](https://github.com/newrelic/newrelic-client-go/blob/cbe3e4cf2b95fd37095bf2ffdc5d61cffaec17e2/pkg/region/region_constants.go#L8-L21) +so (as of 2024/09/14) it returns the region as "US", "EU", "Staging", or "Local". + +In case the region provided does not match these 4, the helper calls `fail` and abort the templating. + +Usage: +```mustache +{{ include "newrelic.common.region.validate" "us" }} +``` + +### `newrelic.common.region` +It reads global and local variables for `region`: +```yaml +global: + region: # Note that this can be empty (nil) or "" (empty string) +region: # Note that this can be empty (nil) or "" (empty string) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in your +values a `region` is defined, the global one is going to be always ignored. + +This function gives protection so it enforces users to give the license key as a value in their +`values.yaml` or specify a global or local `region` value. To understand how the `region` value +works, read the documentation of `newrelic.common.region.validate`. + +The function will change the region from US, EU or Staging based of the license key and the +`nrStaging` toggle. Whichever region is computed from the license/toggle can be overridden by +the `region` value. + +Usage: +```mustache +{{ include "newrelic.common.region" . }} +``` + + + +## _low-data-mode.tpl +### `newrelic.common.lowDataMode` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + lowDataMode: # Note that this is empty (nil) +lowDataMode: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `lowdataMode` is defined, the global one is going to be always ignored. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage: +```mustache +{{ include "newrelic.common.lowDataMode" . }} +``` + + + +## _privileged.tpl +### `newrelic.common.privileged` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + privileged: # Note that this is empty (nil) +privileged: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `privileged` is defined, the global one is going to be always ignored. + +Chart writers could override this and put directly a `true` in the `values.yaml` to override the +default of the common library. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage: +```mustache +{{ include "newrelic.common.privileged" . }} +``` + +### `newrelic.common.privileged.value` +Returns true if privileged mode is enabled or false if not. This is to have the value of privileged ready to be templated. + +Usage: +```mustache +{{ include "newrelic.common.privileged.value" . }} +``` + + + +## _proxy.tpl +### `newrelic.common.proxy` +Returns the proxy URL configured by the user. + +Usage: +```mustache +{{ include "newrelic.common.proxy" . }} +``` + + + +## _security-context.tpl +Use these functions to share the security context among all charts. Useful in clusters that have security enforcing not to +use the root user (like OpenShift) or users that have an admission webhooks. + +The functions are: +* `newrelic.common.securityContext.container` +* `newrelic.common.securityContext.pod` + +Usage: +```mustache +apiVersion: v1 +kind: Pod +metadata: + name: example +spec: + spec: + {{- with include "newrelic.common.securityContext.pod" . }} + securityContext: + {{- . | nindent 8 }} + {{- end }} + + containers: + - name: example + {{- with include "nriKubernetes.securityContext.container" . }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} +``` + + + +## _staging.tpl +### `newrelic.common.nrStaging` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + nrStaging: # Note that this is empty (nil) +nrStaging: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `nrStaging` is defined, the global one is going to be always ignored. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage: +```mustache +{{ include "newrelic.common.nrStaging" . }} +``` + +### `newrelic.common.nrStaging.value` +Returns true if staging is enabled or false if not. This is to have the staging value ready to be templated. + +Usage: +```mustache +{{ include "newrelic.common.nrStaging.value" . }} +``` + + + +## _verbose-log.tpl +### `newrelic.common.verboseLog` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + verboseLog: # Note that this is empty (nil) +verboseLog: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `verboseLog` is defined, the global one is going to be always ignored. + +Usage: +```mustache +{{ include "newrelic.common.verboseLog" . }} +``` + +### `newrelic.common.verboseLog.valueAsBoolean` +Returns true if verbose is enabled or false if not. This is to have the verbose value ready to be templated as a boolean + +Usage: +```mustache +{{ include "newrelic.common.verboseLog.valueAsBoolean" . }} +``` + +### `newrelic.common.verboseLog.valueAsInt` +Returns 1 if verbose is enabled or 0 if not. This is to have the verbose value ready to be templated as an integer + +Usage: +```mustache +{{ include "newrelic.common.verboseLog.valueAsInt" . }} +``` diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/README.md b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/README.md new file mode 100644 index 0000000000..10f08ca677 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/README.md @@ -0,0 +1,106 @@ +# Helm Common library + +The common library is a way to unify the UX through all the Helm charts that implement it. + +The tooling suite that New Relic is huge and growing and this allows to set things globally +and locally for a single chart. + +## Documentation for chart writers + +If you are writing a chart that is going to use this library you can check the [developers guide](/library/common-library/DEVELOPERS.md) to see all +the functions/templates that we have implemented, what they do and how to use them. + +## Values managed globally + +We want to have a seamless experience through all the charts so we created this library that tries to standardize the behaviour +of all the charts. Sadly, because of the complexity of all these integrations, not all the charts behave exactly as expected. + +An example is `newrelic-infrastructure` that ignores `hostNetwork` in the control plane scraper because most of the users has the +control plane listening in the node to `localhost`. + +For each chart that has a special behavior (or further information of the behavior) there is a "chart particularities" section +in its README.md that explains which is the expected behavior. + +At the time of writing this, all the charts from `nri-bundle` except `newrelic-logging` and `synthetics-minion` implements this +library and honors global options as described in this document. + +Here is a list of global options: + +| Global keys | Local keys | Default | Merged[1](#values-managed-globally-1) | Description | +|-------------|------------|---------|--------------------------------------------------|-------------| +| global.cluster | cluster | `""` | | Name of the Kubernetes cluster monitored | +| global.licenseKey | licenseKey | `""` | | This set this license key to use | +| global.customSecretName | customSecretName | `""` | | In case you don't want to have the license key in you values, this allows you to point to a user created secret to get the key from there | +| global.customSecretLicenseKey | customSecretLicenseKey | `""` | | In case you don't want to have the license key in you values, this allows you to point to which secret key is the license key located | +| global.podLabels | podLabels | `{}` | yes | Additional labels for chart pods | +| global.labels | labels | `{}` | yes | Additional labels for chart objects | +| global.priorityClassName | priorityClassName | `""` | | Sets pod's priorityClassName | +| global.hostNetwork | hostNetwork | `false` | | Sets pod's hostNetwork | +| global.dnsConfig | dnsConfig | `{}` | | Sets pod's dnsConfig | +| global.images.registry | See [Further information](#values-managed-globally-2) | `""` | | Changes the registry where to get the images. Useful when there is an internal image cache/proxy | +| global.images.pullSecrets | See [Further information](#values-managed-globally-2) | `[]` | yes | Set secrets to be able to fetch images | +| global.podSecurityContext | podSecurityContext | `{}` | | Sets security context (at pod level) | +| global.containerSecurityContext | containerSecurityContext | `{}` | | Sets security context (at container level) | +| global.affinity | affinity | `{}` | | Sets pod/node affinities | +| global.nodeSelector | nodeSelector | `{}` | | Sets pod's node selector | +| global.tolerations | tolerations | `[]` | | Sets pod's tolerations to node taints | +| global.serviceAccount.create | serviceAccount.create | `true` | | Configures if the service account should be created or not | +| global.serviceAccount.name | serviceAccount.name | name of the release | | Change the name of the service account. This is honored if you disable on this cahrt the creation of the service account so you can use your own. | +| global.serviceAccount.annotations | serviceAccount.annotations | `{}` | yes | Add these annotations to the service account we create | +| global.customAttributes | customAttributes | `{}` | | Adds extra attributes to the cluster and all the metrics emitted to the backend | +| global.fedramp | fedramp | `false` | | Enables FedRAMP | +| global.lowDataMode | lowDataMode | `false` | | Reduces number of metrics sent in order to reduce costs | +| global.privileged | privileged | Depends on the chart | | In each integration it has different behavior. See [Further information](#values-managed-globally-3) but all aims to send less metrics to the backend to try to save costs | +| global.proxy | proxy | `""` | | Configures the integration to send all HTTP/HTTPS request through the proxy in that URL. The URL should have a standard format like `https://user:password@hostname:port` | +| global.nrStaging | nrStaging | `false` | | Send the metrics to the staging backend. Requires a valid staging license key | +| global.verboseLog | verboseLog | `false` | | Sets the debug/trace logs to this integration or all integrations if it is set globally | + +### Further information + +#### 1. Merged + +Merged means that the values from global are not replaced by the local ones. Think in this example: +```yaml +global: + labels: + global: global + hostNetwork: true + nodeSelector: + global: global + +labels: + local: local +nodeSelector: + local: local +hostNetwork: false +``` + +This values will template `hostNetwork` to `false`, a map of labels `{ "global": "global", "local": "local" }` and a `nodeSelector` with +`{ "local": "local" }`. + +As Helm by default merges all the maps it could be confusing that we have two behaviors (merging `labels` and replacing `nodeSelector`) +the `values` from global to local. This is the rationale behind this: +* `hostNetwork` is templated to `false` because is overriding the value defined globally. +* `labels` are merged because the user may want to label all the New Relic pods at once and label other solution pods differently for + clarity' sake. +* `nodeSelector` does not merge as `labels` because could make it harder to overwrite/delete a selector that comes from global because + of the logic that Helm follows merging maps. + + +#### 2. Fine grain registries + +Some charts only have 1 image while others that can have 2 or more images. The local path for the registry can change depending +on the chart itself. + +As this is mostly unique per helm chart, you should take a look to the chart's values table (or directly to the `values.yaml` file to see all the +images that you can change. + +This should only be needed if you have an advanced setup that forces you to have granularity enough to force a proxy/cache registry per integration. + + + +#### 3. Privileged mode + +By default, from the common library, the privileged mode is set to false. But most of the helm charts require this to be true to fetch more +metrics so could see a true in some charts. The consequences of the privileged mode differ from one chart to another so for each chart that +honors the privileged mode toggle should be a section in the README explaining which is the behavior with it enabled or disabled. diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_affinity.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_affinity.tpl new file mode 100644 index 0000000000..1b2636754e --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_affinity.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod affinity */ -}} +{{- define "newrelic.common.affinity" -}} + {{- if .Values.affinity -}} + {{- toYaml .Values.affinity -}} + {{- else if .Values.global -}} + {{- if .Values.global.affinity -}} + {{- toYaml .Values.global.affinity -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_agent-config.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_agent-config.tpl new file mode 100644 index 0000000000..9c32861a02 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_agent-config.tpl @@ -0,0 +1,26 @@ +{{/* +This helper should return the defaults that all agents should have +*/}} +{{- define "newrelic.common.agentConfig.defaults" -}} +{{- if include "newrelic.common.verboseLog" . }} +log: + level: trace +{{- end }} + +{{- if (include "newrelic.common.nrStaging" . ) }} +staging: true +{{- end }} + +{{- with include "newrelic.common.proxy" . }} +proxy: {{ . | quote }} +{{- end }} + +{{- with include "newrelic.common.fedramp.enabled" . }} +fedramp: {{ . }} +{{- end }} + +{{- with fromYaml ( include "newrelic.common.customAttributes" . ) }} +custom_attributes: + {{- toYaml . | nindent 2 }} +{{- end }} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_cluster.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_cluster.tpl new file mode 100644 index 0000000000..0197dd35a3 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_cluster.tpl @@ -0,0 +1,15 @@ +{{/* +Return the cluster +*/}} +{{- define "newrelic.common.cluster" -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} + +{{- if .Values.cluster -}} + {{- .Values.cluster -}} +{{- else if $global.cluster -}} + {{- $global.cluster -}} +{{- else -}} + {{ fail "There is not cluster name definition set neither in `.global.cluster' nor `.cluster' in your values.yaml. Cluster name is required." }} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_custom-attributes.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_custom-attributes.tpl new file mode 100644 index 0000000000..92020719c3 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_custom-attributes.tpl @@ -0,0 +1,17 @@ +{{/* +This will render custom attributes as a YAML ready to be templated or be used with `fromYaml`. +*/}} +{{- define "newrelic.common.customAttributes" -}} +{{- $customAttributes := dict -}} + +{{- $global := index .Values "global" | default dict -}} +{{- if $global.customAttributes -}} +{{- $customAttributes = mergeOverwrite $customAttributes $global.customAttributes -}} +{{- end -}} + +{{- if .Values.customAttributes -}} +{{- $customAttributes = mergeOverwrite $customAttributes .Values.customAttributes -}} +{{- end -}} + +{{- toYaml $customAttributes -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_dnsconfig.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_dnsconfig.tpl new file mode 100644 index 0000000000..d4e40aa8af --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_dnsconfig.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod dnsConfig */ -}} +{{- define "newrelic.common.dnsConfig" -}} + {{- if .Values.dnsConfig -}} + {{- toYaml .Values.dnsConfig -}} + {{- else if .Values.global -}} + {{- if .Values.global.dnsConfig -}} + {{- toYaml .Values.global.dnsConfig -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_fedramp.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_fedramp.tpl new file mode 100644 index 0000000000..9df8d6b5e9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_fedramp.tpl @@ -0,0 +1,25 @@ +{{- /* Defines the fedRAMP flag */ -}} +{{- define "newrelic.common.fedramp.enabled" -}} + {{- if .Values.fedramp -}} + {{- if .Values.fedramp.enabled -}} + {{- .Values.fedramp.enabled -}} + {{- end -}} + {{- else if .Values.global -}} + {{- if .Values.global.fedramp -}} + {{- if .Values.global.fedramp.enabled -}} + {{- .Values.global.fedramp.enabled -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} + + + +{{- /* Return FedRAMP value directly ready to be templated */ -}} +{{- define "newrelic.common.fedramp.enabled.value" -}} +{{- if include "newrelic.common.fedramp.enabled" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_hostnetwork.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_hostnetwork.tpl new file mode 100644 index 0000000000..4cf017ef7e --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_hostnetwork.tpl @@ -0,0 +1,39 @@ +{{- /* +Abstraction of the hostNetwork toggle. +This helper allows to override the global `.global.hostNetwork` with the value of `.hostNetwork`. +Returns "true" if `hostNetwork` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.hostNetwork" -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} + +{{- /* +`get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs + +We also want only to return when this is true, returning `false` here will template "false" (string) when doing +an `(include "newrelic.common.hostNetwork" .)`, which is not an "empty string" so it is `true` if it is used +as an evaluation somewhere else. +*/ -}} +{{- if get .Values "hostNetwork" | kindIs "bool" -}} + {{- if .Values.hostNetwork -}} + {{- .Values.hostNetwork -}} + {{- end -}} +{{- else if get $global "hostNetwork" | kindIs "bool" -}} + {{- if $global.hostNetwork -}} + {{- $global.hostNetwork -}} + {{- end -}} +{{- end -}} +{{- end -}} + + +{{- /* +Abstraction of the hostNetwork toggle. +This helper abstracts the function "newrelic.common.hostNetwork" to return true or false directly. +*/ -}} +{{- define "newrelic.common.hostNetwork.value" -}} +{{- if include "newrelic.common.hostNetwork" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_images.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_images.tpl new file mode 100644 index 0000000000..d4fb432905 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_images.tpl @@ -0,0 +1,94 @@ +{{- /* +Return the proper image name +{{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.path.to.the.image "defaultRegistry" "your.private.registry.tld" "context" .) }} +*/ -}} +{{- define "newrelic.common.images.image" -}} + {{- $registryName := include "newrelic.common.images.registry" ( dict "imageRoot" .imageRoot "defaultRegistry" .defaultRegistry "context" .context ) -}} + {{- $repositoryName := include "newrelic.common.images.repository" .imageRoot -}} + {{- $tag := include "newrelic.common.images.tag" ( dict "imageRoot" .imageRoot "context" .context) -}} + + {{- if $registryName -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag | quote -}} + {{- else -}} + {{- printf "%s:%s" $repositoryName $tag | quote -}} + {{- end -}} +{{- end -}} + + + +{{- /* +Return the proper image registry +{{ include "newrelic.common.images.registry" ( dict "imageRoot" .Values.path.to.the.image "defaultRegistry" "your.private.registry.tld" "context" .) }} +*/ -}} +{{- define "newrelic.common.images.registry" -}} +{{- $globalRegistry := "" -}} +{{- if .context.Values.global -}} + {{- if .context.Values.global.images -}} + {{- with .context.Values.global.images.registry -}} + {{- $globalRegistry = . -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- $localRegistry := "" -}} +{{- if .imageRoot.registry -}} + {{- $localRegistry = .imageRoot.registry -}} +{{- end -}} + +{{- $registry := $localRegistry | default $globalRegistry | default .defaultRegistry -}} +{{- if $registry -}} + {{- $registry -}} +{{- end -}} +{{- end -}} + + + +{{- /* +Return the proper image repository +{{ include "newrelic.common.images.repository" .Values.path.to.the.image }} +*/ -}} +{{- define "newrelic.common.images.repository" -}} + {{- .repository -}} +{{- end -}} + + + +{{- /* +Return the proper image tag +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.path.to.the.image "context" .) }} +*/ -}} +{{- define "newrelic.common.images.tag" -}} + {{- .imageRoot.tag | default .context.Chart.AppVersion | toString -}} +{{- end -}} + + + +{{- /* +Return the proper Image Pull Registry Secret Names evaluating values as templates +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" (list .Values.path.to.the.images.pullSecrets1, .Values.path.to.the.images.pullSecrets2) "context" .) }} +*/ -}} +{{- define "newrelic.common.images.renderPullSecrets" -}} + {{- $flatlist := list }} + + {{- if .context.Values.global -}} + {{- if .context.Values.global.images -}} + {{- if .context.Values.global.images.pullSecrets -}} + {{- range .context.Values.global.images.pullSecrets -}} + {{- $flatlist = append $flatlist . -}} + {{- end -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- range .pullSecrets -}} + {{- if not (empty .) -}} + {{- range . -}} + {{- $flatlist = append $flatlist . -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- if $flatlist -}} + {{- toYaml $flatlist -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_insights.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_insights.tpl new file mode 100644 index 0000000000..895c377326 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_insights.tpl @@ -0,0 +1,56 @@ +{{/* +Return the name of the secret holding the Insights Key. +*/}} +{{- define "newrelic.common.insightsKey.secretName" -}} +{{- $default := include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "insightskey" ) -}} +{{- include "newrelic.common.insightsKey._customSecretName" . | default $default -}} +{{- end -}} + +{{/* +Return the name key for the Insights Key inside the secret. +*/}} +{{- define "newrelic.common.insightsKey.secretKeyName" -}} +{{- include "newrelic.common.insightsKey._customSecretKey" . | default "insightsKey" -}} +{{- end -}} + +{{/* +Return local insightsKey if set, global otherwise. +This helper is for internal use. +*/}} +{{- define "newrelic.common.insightsKey._licenseKey" -}} +{{- if .Values.insightsKey -}} + {{- .Values.insightsKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.insightsKey -}} + {{- .Values.global.insightsKey -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name of the secret holding the Insights Key. +This helper is for internal use. +*/}} +{{- define "newrelic.common.insightsKey._customSecretName" -}} +{{- if .Values.customInsightsKeySecretName -}} + {{- .Values.customInsightsKeySecretName -}} +{{- else if .Values.global -}} + {{- if .Values.global.customInsightsKeySecretName -}} + {{- .Values.global.customInsightsKeySecretName -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name key for the Insights Key inside the secret. +This helper is for internal use. +*/}} +{{- define "newrelic.common.insightsKey._customSecretKey" -}} +{{- if .Values.customInsightsKeySecretKey -}} + {{- .Values.customInsightsKeySecretKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.customInsightsKeySecretKey }} + {{- .Values.global.customInsightsKeySecretKey -}} + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_insights_secret.yaml.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_insights_secret.yaml.tpl new file mode 100644 index 0000000000..556caa6ca6 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_insights_secret.yaml.tpl @@ -0,0 +1,21 @@ +{{/* +Renders the insights key secret if user has not specified a custom secret. +*/}} +{{- define "newrelic.common.insightsKey.secret" }} +{{- if not (include "newrelic.common.insightsKey._customSecretName" .) }} +{{- /* Fail if licenseKey is empty and required: */ -}} +{{- if not (include "newrelic.common.insightsKey._licenseKey" .) }} + {{- fail "You must specify a insightsKey or a customInsightsSecretName containing it" }} +{{- end }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "newrelic.common.insightsKey.secretName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +data: + {{ include "newrelic.common.insightsKey.secretKeyName" . }}: {{ include "newrelic.common.insightsKey._licenseKey" . | b64enc }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_labels.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_labels.tpl new file mode 100644 index 0000000000..b025948285 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_labels.tpl @@ -0,0 +1,54 @@ +{{/* +This will render the labels that should be used in all the manifests used by the helm chart. +*/}} +{{- define "newrelic.common.labels" -}} +{{- $global := index .Values "global" | default dict -}} + +{{- $chart := dict "helm.sh/chart" (include "newrelic.common.naming.chart" . ) -}} +{{- $managedBy := dict "app.kubernetes.io/managed-by" .Release.Service -}} +{{- $selectorLabels := fromYaml (include "newrelic.common.labels.selectorLabels" . ) -}} + +{{- $labels := mustMergeOverwrite $chart $managedBy $selectorLabels -}} +{{- if .Chart.AppVersion -}} +{{- $labels = mustMergeOverwrite $labels (dict "app.kubernetes.io/version" .Chart.AppVersion) -}} +{{- end -}} + +{{- $globalUserLabels := $global.labels | default dict -}} +{{- $localUserLabels := .Values.labels | default dict -}} + +{{- $labels = mustMergeOverwrite $labels $globalUserLabels $localUserLabels -}} + +{{- toYaml $labels -}} +{{- end -}} + + + +{{/* +This will render the labels that should be used in deployments/daemonsets template pods as a selector. +*/}} +{{- define "newrelic.common.labels.selectorLabels" -}} +{{- $name := dict "app.kubernetes.io/name" ( include "newrelic.common.naming.name" . ) -}} +{{- $instance := dict "app.kubernetes.io/instance" .Release.Name -}} + +{{- $selectorLabels := mustMergeOverwrite $name $instance -}} + +{{- toYaml $selectorLabels -}} +{{- end }} + + + +{{/* +Pod labels +*/}} +{{- define "newrelic.common.labels.podLabels" -}} +{{- $selectorLabels := fromYaml (include "newrelic.common.labels.selectorLabels" . ) -}} + +{{- $global := index .Values "global" | default dict -}} +{{- $globalPodLabels := $global.podLabels | default dict }} + +{{- $localPodLabels := .Values.podLabels | default dict }} + +{{- $podLabels := mustMergeOverwrite $selectorLabels $globalPodLabels $localPodLabels -}} + +{{- toYaml $podLabels -}} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_license.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_license.tpl new file mode 100644 index 0000000000..cb349f6bb6 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_license.tpl @@ -0,0 +1,68 @@ +{{/* +Return the name of the secret holding the License Key. +*/}} +{{- define "newrelic.common.license.secretName" -}} +{{- $default := include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "license" ) -}} +{{- include "newrelic.common.license._customSecretName" . | default $default -}} +{{- end -}} + +{{/* +Return the name key for the License Key inside the secret. +*/}} +{{- define "newrelic.common.license.secretKeyName" -}} +{{- include "newrelic.common.license._customSecretKey" . | default "licenseKey" -}} +{{- end -}} + +{{/* +Return local licenseKey if set, global otherwise. +This helper is for internal use. +*/}} +{{- define "newrelic.common.license._licenseKey" -}} +{{- if .Values.licenseKey -}} + {{- .Values.licenseKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.licenseKey -}} + {{- .Values.global.licenseKey -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name of the secret holding the License Key. +This helper is for internal use. +*/}} +{{- define "newrelic.common.license._customSecretName" -}} +{{- if .Values.customSecretName -}} + {{- .Values.customSecretName -}} +{{- else if .Values.global -}} + {{- if .Values.global.customSecretName -}} + {{- .Values.global.customSecretName -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name key for the License Key inside the secret. +This helper is for internal use. +*/}} +{{- define "newrelic.common.license._customSecretKey" -}} +{{- if .Values.customSecretLicenseKey -}} + {{- .Values.customSecretLicenseKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.customSecretLicenseKey }} + {{- .Values.global.customSecretLicenseKey -}} + {{- end -}} +{{- end -}} +{{- end -}} + + + +{{/* +Return empty string (falsehood) or "true" if the user set a custom secret for the license. +This helper is for internal use. +*/}} +{{- define "newrelic.common.license._usesCustomSecret" -}} +{{- if or (include "newrelic.common.license._customSecretName" .) (include "newrelic.common.license._customSecretKey" .) -}} +true +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_license_secret.yaml.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_license_secret.yaml.tpl new file mode 100644 index 0000000000..610a0a3370 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_license_secret.yaml.tpl @@ -0,0 +1,21 @@ +{{/* +Renders the license key secret if user has not specified a custom secret. +*/}} +{{- define "newrelic.common.license.secret" }} +{{- if not (include "newrelic.common.license._customSecretName" .) }} +{{- /* Fail if licenseKey is empty and required: */ -}} +{{- if not (include "newrelic.common.license._licenseKey" .) }} + {{- fail "You must specify a licenseKey or a customSecretName containing it" }} +{{- end }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "newrelic.common.license.secretName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +data: + {{ include "newrelic.common.license.secretKeyName" . }}: {{ include "newrelic.common.license._licenseKey" . | b64enc }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_low-data-mode.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_low-data-mode.tpl new file mode 100644 index 0000000000..3dd55ef2ff --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_low-data-mode.tpl @@ -0,0 +1,26 @@ +{{- /* +Abstraction of the lowDataMode toggle. +This helper allows to override the global `.global.lowDataMode` with the value of `.lowDataMode`. +Returns "true" if `lowDataMode` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.lowDataMode" -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if (get .Values "lowDataMode" | kindIs "bool") -}} + {{- if .Values.lowDataMode -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.lowDataMode" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.lowDataMode -}} + {{- end -}} +{{- else -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "lowDataMode" | kindIs "bool" -}} + {{- if $global.lowDataMode -}} + {{- $global.lowDataMode -}} + {{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_naming.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_naming.tpl new file mode 100644 index 0000000000..19fa92648c --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_naming.tpl @@ -0,0 +1,73 @@ +{{/* +This is an function to be called directly with a string just to truncate strings to +63 chars because some Kubernetes name fields are limited to that. +*/}} +{{- define "newrelic.common.naming.truncateToDNS" -}} +{{- . | trunc 63 | trimSuffix "-" }} +{{- end }} + + + +{{- /* +Given a name and a suffix returns a 'DNS Valid' which always include the suffix, truncating the name if needed. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If suffix is too long it gets truncated but it always takes precedence over name, so a 63 chars suffix would suppress the name. +Usage: +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" "" "suffix" "my-suffix" ) }} +*/ -}} +{{- define "newrelic.common.naming.truncateToDNSWithSuffix" -}} +{{- $suffix := (include "newrelic.common.naming.truncateToDNS" .suffix) -}} +{{- $maxLen := (max (sub 63 (add1 (len $suffix))) 0) -}} {{- /* We prepend "-" to the suffix so an additional character is needed */ -}} + +{{- $newName := .name | trunc ($maxLen | int) | trimSuffix "-" -}} +{{- if $newName -}} +{{- printf "%s-%s" $newName $suffix -}} +{{- else -}} +{{ $suffix }} +{{- end -}} + +{{- end -}} + + + +{{/* +Expand the name of the chart. +Uses the Chart name by default if nameOverride is not set. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "newrelic.common.naming.name" -}} +{{- $name := .Values.nameOverride | default .Chart.Name -}} +{{- include "newrelic.common.naming.truncateToDNS" $name -}} +{{- end }} + + + +{{/* +Create a default fully qualified app name. +By default the full name will be "" just in if it has the chart name included in that, if not +it will be concatenated like "-". This could change if fullnameOverride or +nameOverride are set. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "newrelic.common.naming.fullname" -}} +{{- $name := include "newrelic.common.naming.name" . -}} + +{{- if .Values.fullnameOverride -}} + {{- $name = .Values.fullnameOverride -}} +{{- else if not (contains $name .Release.Name) -}} + {{- $name = printf "%s-%s" .Release.Name $name -}} +{{- end -}} + +{{- include "newrelic.common.naming.truncateToDNS" $name -}} + +{{- end -}} + + + +{{/* +Create chart name and version as used by the chart label. +This function should not be used for naming objects. Use "common.naming.{name,fullname}" instead. +*/}} +{{- define "newrelic.common.naming.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_nodeselector.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_nodeselector.tpl new file mode 100644 index 0000000000..d488873412 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_nodeselector.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod nodeSelector */ -}} +{{- define "newrelic.common.nodeSelector" -}} + {{- if .Values.nodeSelector -}} + {{- toYaml .Values.nodeSelector -}} + {{- else if .Values.global -}} + {{- if .Values.global.nodeSelector -}} + {{- toYaml .Values.global.nodeSelector -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_priority-class-name.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_priority-class-name.tpl new file mode 100644 index 0000000000..50182b7343 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_priority-class-name.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the pod priorityClassName */ -}} +{{- define "newrelic.common.priorityClassName" -}} + {{- if .Values.priorityClassName -}} + {{- .Values.priorityClassName -}} + {{- else if .Values.global -}} + {{- if .Values.global.priorityClassName -}} + {{- .Values.global.priorityClassName -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_privileged.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_privileged.tpl new file mode 100644 index 0000000000..f3ae814dd9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_privileged.tpl @@ -0,0 +1,28 @@ +{{- /* +This is a helper that returns whether the chart should assume the user is fine deploying privileged pods. +*/ -}} +{{- define "newrelic.common.privileged" -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists. */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if get .Values "privileged" | kindIs "bool" -}} + {{- if .Values.privileged -}} + {{- .Values.privileged -}} + {{- end -}} +{{- else if get $global "privileged" | kindIs "bool" -}} + {{- if $global.privileged -}} + {{- $global.privileged -}} + {{- end -}} +{{- end -}} +{{- end -}} + + + +{{- /* Return directly "true" or "false" based in the exist of "newrelic.common.privileged" */ -}} +{{- define "newrelic.common.privileged.value" -}} +{{- if include "newrelic.common.privileged" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_proxy.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_proxy.tpl new file mode 100644 index 0000000000..60f34c7ec1 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_proxy.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the proxy */ -}} +{{- define "newrelic.common.proxy" -}} + {{- if .Values.proxy -}} + {{- .Values.proxy -}} + {{- else if .Values.global -}} + {{- if .Values.global.proxy -}} + {{- .Values.global.proxy -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_region.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_region.tpl new file mode 100644 index 0000000000..bdcacf3235 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_region.tpl @@ -0,0 +1,74 @@ +{{/* +Return the region that is being used by the user +*/}} +{{- define "newrelic.common.region" -}} +{{- if and (include "newrelic.common.license._usesCustomSecret" .) (not (include "newrelic.common.region._fromValues" .)) -}} + {{- fail "This Helm Chart is not able to compute the region. You must specify a .global.region or .region if the license is set using a custom secret." -}} +{{- end -}} + +{{- /* Defaults */ -}} +{{- $region := "us" -}} +{{- if include "newrelic.common.nrStaging" . -}} + {{- $region = "staging" -}} +{{- else if include "newrelic.common.region._isEULicenseKey" . -}} + {{- $region = "eu" -}} +{{- end -}} + +{{- include "newrelic.common.region.validate" (include "newrelic.common.region._fromValues" . | default $region ) -}} +{{- end -}} + + + +{{/* +Returns the region from the values if valid. This only return the value from the `values.yaml`. +More intelligence should be used to compute the region. + +Usage: `include "newrelic.common.region.validate" "us"` +*/}} +{{- define "newrelic.common.region.validate" -}} +{{- /* Ref: https://github.com/newrelic/newrelic-client-go/blob/cbe3e4cf2b95fd37095bf2ffdc5d61cffaec17e2/pkg/region/region_constants.go#L8-L21 */ -}} +{{- $region := . | lower -}} +{{- if eq $region "us" -}} + US +{{- else if eq $region "eu" -}} + EU +{{- else if eq $region "staging" -}} + Staging +{{- else if eq $region "local" -}} + Local +{{- else -}} + {{- fail (printf "the region provided is not valid: %s not in \"US\" \"EU\" \"Staging\" \"Local\"" .) -}} +{{- end -}} +{{- end -}} + + + +{{/* +Returns the region from the values. This only return the value from the `values.yaml`. +More intelligence should be used to compute the region. +This helper is for internal use. +*/}} +{{- define "newrelic.common.region._fromValues" -}} +{{- if .Values.region -}} + {{- .Values.region -}} +{{- else if .Values.global -}} + {{- if .Values.global.region -}} + {{- .Values.global.region -}} + {{- end -}} +{{- end -}} +{{- end -}} + + + +{{/* +Return empty string (falsehood) or "true" if the license is for EU region. +This helper is for internal use. +*/}} +{{- define "newrelic.common.region._isEULicenseKey" -}} +{{- if not (include "newrelic.common.license._usesCustomSecret" .) -}} + {{- $license := include "newrelic.common.license._licenseKey" . -}} + {{- if hasPrefix "eu" $license -}} + true + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_security-context.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_security-context.tpl new file mode 100644 index 0000000000..9edfcabfd0 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_security-context.tpl @@ -0,0 +1,23 @@ +{{- /* Defines the container securityContext context */ -}} +{{- define "newrelic.common.securityContext.container" -}} +{{- $global := index .Values "global" | default dict -}} + +{{- if .Values.containerSecurityContext -}} + {{- toYaml .Values.containerSecurityContext -}} +{{- else if $global.containerSecurityContext -}} + {{- toYaml $global.containerSecurityContext -}} +{{- end -}} +{{- end -}} + + + +{{- /* Defines the pod securityContext context */ -}} +{{- define "newrelic.common.securityContext.pod" -}} +{{- $global := index .Values "global" | default dict -}} + +{{- if .Values.podSecurityContext -}} + {{- toYaml .Values.podSecurityContext -}} +{{- else if $global.podSecurityContext -}} + {{- toYaml $global.podSecurityContext -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_serviceaccount.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_serviceaccount.tpl new file mode 100644 index 0000000000..2d352f6ea9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_serviceaccount.tpl @@ -0,0 +1,90 @@ +{{- /* Defines if the service account has to be created or not */ -}} +{{- define "newrelic.common.serviceAccount.create" -}} +{{- $valueFound := false -}} + +{{- /* Look for a global creation of a service account */ -}} +{{- if get .Values "serviceAccount" | kindIs "map" -}} + {{- if (get .Values.serviceAccount "create" | kindIs "bool") -}} + {{- $valueFound = true -}} + {{- if .Values.serviceAccount.create -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.serviceAccount.name" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.serviceAccount.create -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- /* Look for a local creation of a service account */ -}} +{{- if not $valueFound -}} + {{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} + {{- $global := index .Values "global" | default dict -}} + {{- if get $global "serviceAccount" | kindIs "map" -}} + {{- if get $global.serviceAccount "create" | kindIs "bool" -}} + {{- $valueFound = true -}} + {{- if $global.serviceAccount.create -}} + {{- $global.serviceAccount.create -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- /* In case no serviceAccount value has been found, default to "true" */ -}} +{{- if not $valueFound -}} +true +{{- end -}} +{{- end -}} + + + +{{- /* Defines the name of the service account */ -}} +{{- define "newrelic.common.serviceAccount.name" -}} +{{- $localServiceAccount := "" -}} +{{- if get .Values "serviceAccount" | kindIs "map" -}} + {{- if (get .Values.serviceAccount "name" | kindIs "string") -}} + {{- $localServiceAccount = .Values.serviceAccount.name -}} + {{- end -}} +{{- end -}} + +{{- $globalServiceAccount := "" -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "serviceAccount" | kindIs "map" -}} + {{- if get $global.serviceAccount "name" | kindIs "string" -}} + {{- $globalServiceAccount = $global.serviceAccount.name -}} + {{- end -}} +{{- end -}} + +{{- if (include "newrelic.common.serviceAccount.create" .) -}} + {{- $localServiceAccount | default $globalServiceAccount | default (include "newrelic.common.naming.fullname" .) -}} +{{- else -}} + {{- $localServiceAccount | default $globalServiceAccount | default "default" -}} +{{- end -}} +{{- end -}} + + + +{{- /* Merge the global and local annotations for the service account */ -}} +{{- define "newrelic.common.serviceAccount.annotations" -}} +{{- $localServiceAccount := dict -}} +{{- if get .Values "serviceAccount" | kindIs "map" -}} + {{- if get .Values.serviceAccount "annotations" -}} + {{- $localServiceAccount = .Values.serviceAccount.annotations -}} + {{- end -}} +{{- end -}} + +{{- $globalServiceAccount := dict -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "serviceAccount" | kindIs "map" -}} + {{- if get $global.serviceAccount "annotations" -}} + {{- $globalServiceAccount = $global.serviceAccount.annotations -}} + {{- end -}} +{{- end -}} + +{{- $merged := mustMergeOverwrite $globalServiceAccount $localServiceAccount -}} + +{{- if $merged -}} + {{- toYaml $merged -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_staging.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_staging.tpl new file mode 100644 index 0000000000..bd9ad09bb9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_staging.tpl @@ -0,0 +1,39 @@ +{{- /* +Abstraction of the nrStaging toggle. +This helper allows to override the global `.global.nrStaging` with the value of `.nrStaging`. +Returns "true" if `nrStaging` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.nrStaging" -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if (get .Values "nrStaging" | kindIs "bool") -}} + {{- if .Values.nrStaging -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.nrStaging" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.nrStaging -}} + {{- end -}} +{{- else -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "nrStaging" | kindIs "bool" -}} + {{- if $global.nrStaging -}} + {{- $global.nrStaging -}} + {{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} + + + +{{- /* +Returns "true" of "false" directly instead of empty string (Helm falsiness) based on the exit of "newrelic.common.nrStaging" +*/ -}} +{{- define "newrelic.common.nrStaging.value" -}} +{{- if include "newrelic.common.nrStaging" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_tolerations.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_tolerations.tpl new file mode 100644 index 0000000000..e016b38e27 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_tolerations.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod tolerations */ -}} +{{- define "newrelic.common.tolerations" -}} + {{- if .Values.tolerations -}} + {{- toYaml .Values.tolerations -}} + {{- else if .Values.global -}} + {{- if .Values.global.tolerations -}} + {{- toYaml .Values.global.tolerations -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_userkey.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_userkey.tpl new file mode 100644 index 0000000000..982ea8e09d --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_userkey.tpl @@ -0,0 +1,56 @@ +{{/* +Return the name of the secret holding the API Key. +*/}} +{{- define "newrelic.common.userKey.secretName" -}} +{{- $default := include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "userkey" ) -}} +{{- include "newrelic.common.userKey._customSecretName" . | default $default -}} +{{- end -}} + +{{/* +Return the name key for the API Key inside the secret. +*/}} +{{- define "newrelic.common.userKey.secretKeyName" -}} +{{- include "newrelic.common.userKey._customSecretKey" . | default "userKey" -}} +{{- end -}} + +{{/* +Return local API Key if set, global otherwise. +This helper is for internal use. +*/}} +{{- define "newrelic.common.userKey._userKey" -}} +{{- if .Values.userKey -}} + {{- .Values.userKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.userKey -}} + {{- .Values.global.userKey -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name of the secret holding the API Key. +This helper is for internal use. +*/}} +{{- define "newrelic.common.userKey._customSecretName" -}} +{{- if .Values.customUserKeySecretName -}} + {{- .Values.customUserKeySecretName -}} +{{- else if .Values.global -}} + {{- if .Values.global.customUserKeySecretName -}} + {{- .Values.global.customUserKeySecretName -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name key for the API Key inside the secret. +This helper is for internal use. +*/}} +{{- define "newrelic.common.userKey._customSecretKey" -}} +{{- if .Values.customUserKeySecretKey -}} + {{- .Values.customUserKeySecretKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.customUserKeySecretKey }} + {{- .Values.global.customUserKeySecretKey -}} + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_userkey_secret.yaml.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_userkey_secret.yaml.tpl new file mode 100644 index 0000000000..b979856548 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_userkey_secret.yaml.tpl @@ -0,0 +1,21 @@ +{{/* +Renders the user key secret if user has not specified a custom secret. +*/}} +{{- define "newrelic.common.userKey.secret" }} +{{- if not (include "newrelic.common.userKey._customSecretName" .) }} +{{- /* Fail if user key is empty and required: */ -}} +{{- if not (include "newrelic.common.userKey._userKey" .) }} + {{- fail "You must specify a userKey or a customUserKeySecretName containing it" }} +{{- end }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "newrelic.common.userKey.secretName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +data: + {{ include "newrelic.common.userKey.secretKeyName" . }}: {{ include "newrelic.common.userKey._userKey" . | b64enc }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_verbose-log.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_verbose-log.tpl new file mode 100644 index 0000000000..2286d46815 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/templates/_verbose-log.tpl @@ -0,0 +1,54 @@ +{{- /* +Abstraction of the verbose toggle. +This helper allows to override the global `.global.verboseLog` with the value of `.verboseLog`. +Returns "true" if `verbose` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.verboseLog" -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if (get .Values "verboseLog" | kindIs "bool") -}} + {{- if .Values.verboseLog -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.verboseLog" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.verboseLog -}} + {{- end -}} +{{- else -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "verboseLog" | kindIs "bool" -}} + {{- if $global.verboseLog -}} + {{- $global.verboseLog -}} + {{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} + + + +{{- /* +Abstraction of the verbose toggle. +This helper abstracts the function "newrelic.common.verboseLog" to return true or false directly. +*/ -}} +{{- define "newrelic.common.verboseLog.valueAsBoolean" -}} +{{- if include "newrelic.common.verboseLog" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} + + + +{{- /* +Abstraction of the verbose toggle. +This helper abstracts the function "newrelic.common.verboseLog" to return 1 or 0 directly. +*/ -}} +{{- define "newrelic.common.verboseLog.valueAsInt" -}} +{{- if include "newrelic.common.verboseLog" . -}} +1 +{{- else -}} +0 +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/values.yaml new file mode 100644 index 0000000000..75e2d112ad --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/charts/common-library/values.yaml @@ -0,0 +1 @@ +# values are not needed for the library chart, however this file is still needed for helm lint to work. diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/ci/test-values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/ci/test-values.yaml new file mode 100644 index 0000000000..6f79dea930 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/ci/test-values.yaml @@ -0,0 +1,5 @@ +cluster: test-cluster + +image: + repository: e2e/metadata-injection + tag: test # Defaults to AppVersion diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/NOTES.txt b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/NOTES.txt new file mode 100644 index 0000000000..544124d11e --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/NOTES.txt @@ -0,0 +1,23 @@ +Your deployment of the New Relic metadata injection webhook is complete. You can check on the progress of this by running the following command: + + kubectl get deployments -o wide -w --namespace {{ .Release.Namespace }} {{ template "newrelic.common.naming.fullname" . }} + +{{- if .Values.customTLSCertificate }} +You have configure the chart to use a custom tls certificate, make sure to read the 'Manage custom certificates' section of the official docs to find the instructions on how to finish setting up the webhook. + +https://docs.newrelic.com/docs/integrations/kubernetes-integration/link-your-applications/link-your-applications-kubernetes#configure-injection +{{- end }} + +To validate the injection of metadata create a dummy pod containing Busybox by running: + + kubectl create -f https://git.io/vPieo + +Check if New Relic environment variables were injected: + + kubectl exec busybox0 -- env | grep NEW_RELIC_METADATA_KUBERNETES + + NEW_RELIC_METADATA_KUBERNETES_CLUSTER_NAME=fsi + NEW_RELIC_METADATA_KUBERNETES_NODE_NAME=nodea + NEW_RELIC_METADATA_KUBERNETES_NAMESPACE_NAME=default + NEW_RELIC_METADATA_KUBERNETES_POD_NAME=busybox0 + NEW_RELIC_METADATA_KUBERNETES_CONTAINER_NAME=busybox diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/_helpers.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/_helpers.tpl new file mode 100644 index 0000000000..54a23e9811 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/_helpers.tpl @@ -0,0 +1,72 @@ +{{/* vim: set filetype=mustache: */}} + +{{- /* Allow to change pod defaults dynamically */ -}} +{{- define "nri-metadata-injection.securityContext.pod" -}} +{{- if include "newrelic.common.securityContext.pod" . -}} +{{- include "newrelic.common.securityContext.pod" . -}} +{{- else -}} +fsGroup: 1001 +runAsUser: 1001 +runAsGroup: 1001 +{{- end -}} +{{- end -}} + +{{- /* +Naming helpers +*/ -}} + +{{- define "nri-metadata-injection.name.admission" -}} +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.name" .) "suffix" "admission") }} +{{- end -}} + +{{- define "nri-metadata-injection.fullname.admission" -}} +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "admission") }} +{{- end -}} + +{{- define "nri-metadata-injection.fullname.admission.serviceAccount" -}} +{{- if include "newrelic.common.serviceAccount.create" . -}} + {{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "admission") }} +{{- else -}} + {{ include "newrelic.common.serviceAccount.name" . }} +{{- end -}} +{{- end -}} + +{{- define "nri-metadata-injection.name.admission-create" -}} +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.name" .) "suffix" "admission-create") }} +{{- end -}} + +{{- define "nri-metadata-injection.fullname.admission-create" -}} +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "admission-create") }} +{{- end -}} + +{{- define "nri-metadata-injection.name.admission-patch" -}} +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.name" .) "suffix" "admission-patch") }} +{{- end -}} + +{{- define "nri-metadata-injection.fullname.admission-patch" -}} +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "admission-patch") }} +{{- end -}} + +{{- define "nri-metadata-injection.name.self-signed-issuer" -}} +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.name" .) "suffix" "self-signed-issuer") }} +{{- end -}} + +{{- define "nri-metadata-injection.fullname.self-signed-issuer" -}} +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "self-signed-issuer") }} +{{- end -}} + +{{- define "nri-metadata-injection.name.root-issuer" -}} +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.name" .) "suffix" "root-issuer") }} +{{- end -}} + +{{- define "nri-metadata-injection.fullname.root-issuer" -}} +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "root-issuer") }} +{{- end -}} + +{{- define "nri-metadata-injection.name.webhook-cert" -}} +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.name" .) "suffix" "webhook-cert") }} +{{- end -}} + +{{- define "nri-metadata-injection.fullname.webhook-cert" -}} +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "webhook-cert") }} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/clusterrole.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/clusterrole.yaml new file mode 100644 index 0000000000..275b597c8d --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/clusterrole.yaml @@ -0,0 +1,27 @@ +{{- if (and (not .Values.customTLSCertificate) (not .Values.certManager.enabled)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "nri-metadata-injection.fullname.admission" . }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + app: {{ include "newrelic.common.naming.name" $ }}-admission + {{- include "newrelic.common.labels" . | nindent 4 }} +rules: + - apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + verbs: + - get + - update +{{- if .Values.rbac.pspEnabled }} + - apiGroups: ['policy'] + resources: ['podsecuritypolicies'] + verbs: ['use'] + resourceNames: + - {{ include "nri-metadata-injection.fullname.admission" . }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/clusterrolebinding.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/clusterrolebinding.yaml new file mode 100644 index 0000000000..cf846745ed --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/clusterrolebinding.yaml @@ -0,0 +1,20 @@ +{{- if (and (not .Values.customTLSCertificate) (not .Values.certManager.enabled)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "nri-metadata-injection.fullname.admission" . }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + app: {{ include "nri-metadata-injection.name.admission" . }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "nri-metadata-injection.fullname.admission" . }} +subjects: + - kind: ServiceAccount + name: {{ include "nri-metadata-injection.fullname.admission.serviceAccount" . }} + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/job-createSecret.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/job-createSecret.yaml new file mode 100644 index 0000000000..a04f279353 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/job-createSecret.yaml @@ -0,0 +1,61 @@ +{{- if (and (not .Values.customTLSCertificate) (not .Values.certManager.enabled)) }} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "nri-metadata-injection.fullname.admission-create" . }} + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + app: {{ include "nri-metadata-injection.name.admission-create" . }} + {{- include "newrelic.common.labels" . | nindent 4 }} +spec: + template: + metadata: + name: {{ include "nri-metadata-injection.fullname.admission-create" . }} + {{- if .Values.podAnnotations }} + annotations: + {{- toYaml .Values.podAnnotations | nindent 8 }} + {{- end }} + labels: + app: {{ include "nri-metadata-injection.name.admission-create" . }} + {{- include "newrelic.common.labels" . | nindent 8 }} + spec: + {{- with include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" ( list .Values.jobImage.pullSecrets ) "context" .) }} + imagePullSecrets: + {{- . | nindent 8 -}} + {{- end }} + containers: + - name: create + image: {{ include "newrelic.common.images.image" ( dict "defaultRegistry" "registry.k8s.io" "imageRoot" .Values.jobImage "context" .) }} + imagePullPolicy: {{ .Values.jobImage.pullPolicy }} + args: + - create + - --host={{ include "newrelic.common.naming.fullname" . }},{{ include "newrelic.common.naming.fullname" . }}.{{ .Release.Namespace }}.svc + - --namespace={{ .Release.Namespace }} + - --secret-name={{ include "nri-metadata-injection.fullname.admission" . }} + - --cert-name=tls.crt + - --key-name=tls.key + {{- if .Values.jobImage.volumeMounts }} + volumeMounts: + {{- .Values.jobImage.volumeMounts | toYaml | nindent 10 }} + {{- end }} + {{- if .Values.jobImage.volumes }} + volumes: + {{- .Values.jobImage.volumes | toYaml | nindent 8 }} + {{- end }} + restartPolicy: OnFailure + serviceAccountName: {{ include "nri-metadata-injection.fullname.admission.serviceAccount" . }} + securityContext: + runAsGroup: 2000 + runAsNonRoot: true + runAsUser: 2000 + nodeSelector: + kubernetes.io/os: linux + {{ include "newrelic.common.nodeSelector" . | nindent 8 }} + {{- if .Values.tolerations }} + tolerations: + {{- toYaml .Values.tolerations | nindent 8 }} + {{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/job-patchWebhook.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/job-patchWebhook.yaml new file mode 100644 index 0000000000..99374ef355 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/job-patchWebhook.yaml @@ -0,0 +1,61 @@ +{{- if (and (not .Values.customTLSCertificate) (not .Values.certManager.enabled)) }} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "nri-metadata-injection.fullname.admission-patch" . }} + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + app: {{ include "nri-metadata-injection.name.admission-patch" . }} + {{- include "newrelic.common.labels" . | nindent 4 }} +spec: + template: + metadata: + name: {{ include "nri-metadata-injection.fullname.admission-patch" . }} + {{- if .Values.podAnnotations }} + annotations: + {{- toYaml .Values.podAnnotations | nindent 8 }} + {{- end }} + labels: + app: {{ include "nri-metadata-injection.name.admission-patch" . }} + {{- include "newrelic.common.labels" . | nindent 8 }} + spec: + {{- with include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" ( list .Values.jobImage.pullSecrets ) "context" .) }} + imagePullSecrets: + {{- . | nindent 8 -}} + {{- end }} + containers: + - name: patch + image: {{ include "newrelic.common.images.image" ( dict "defaultRegistry" "registry.k8s.io" "imageRoot" .Values.jobImage "context" .) }} + imagePullPolicy: {{ .Values.jobImage.pullPolicy }} + args: + - patch + - --webhook-name={{ include "newrelic.common.naming.fullname" . }} + - --namespace={{ .Release.Namespace }} + - --secret-name={{ include "nri-metadata-injection.fullname.admission" . }} + - --patch-failure-policy=Ignore + - --patch-validating=false + {{- if .Values.jobImage.volumeMounts }} + volumeMounts: + {{- .Values.jobImage.volumeMounts | toYaml | nindent 10 }} + {{- end }} + {{- if .Values.jobImage.volumes }} + volumes: + {{- .Values.jobImage.volumes | toYaml | nindent 8 }} + {{- end }} + restartPolicy: OnFailure + serviceAccountName: {{ include "nri-metadata-injection.fullname.admission.serviceAccount" . }} + securityContext: + runAsGroup: 2000 + runAsNonRoot: true + runAsUser: 2000 + nodeSelector: + kubernetes.io/os: linux + {{ include "newrelic.common.nodeSelector" . | nindent 8 }} + {{- if .Values.tolerations }} + tolerations: + {{- toYaml .Values.tolerations | nindent 8 }} + {{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/psp.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/psp.yaml new file mode 100644 index 0000000000..899ac95fe1 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/psp.yaml @@ -0,0 +1,50 @@ +{{- if (and (not .Values.customTLSCertificate) (not .Values.certManager.enabled) (.Values.rbac.pspEnabled) (.Capabilities.APIVersions.Has "policy/v1beta1/PodSecurityPolicy")) }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ include "nri-metadata-injection.fullname.admission" . }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + app: {{ include "nri-metadata-injection.name.admission" . }} + {{- include "newrelic.common.labels" . | nindent 4 }} +spec: + privileged: false + # Required to prevent escalations to root. + # allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + #requiredDropCapabilities: + # - ALL + # Allow core volume types. + volumes: + - 'configMap' + - 'emptyDir' + - 'projected' + - 'secret' + - 'downwardAPI' + - 'persistentVolumeClaim' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + # Permits the container to run with root privileges as well. + rule: 'RunAsAny' + seLinux: + # This policy assumes the nodes are using AppArmor rather than SELinux. + rule: 'RunAsAny' + supplementalGroups: + rule: 'MustRunAs' + ranges: + # Forbid adding the root group. + - min: 0 + max: 65535 + fsGroup: + rule: 'MustRunAs' + ranges: + # Forbid adding the root group. + - min: 0 + max: 65535 + readOnlyRootFilesystem: false +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/role.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/role.yaml new file mode 100644 index 0000000000..e426702579 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/role.yaml @@ -0,0 +1,21 @@ +{{- if (and (not .Values.customTLSCertificate) (not .Values.certManager.enabled)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "nri-metadata-injection.fullname.admission" . }} + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + app: {{ include "nri-metadata-injection.name.admission" . }} + {{- include "newrelic.common.labels" . | nindent 4 }} +rules: + - apiGroups: + - "" + resources: + - secrets + verbs: + - get + - create +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/rolebinding.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/rolebinding.yaml new file mode 100644 index 0000000000..e73bf472cd --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/rolebinding.yaml @@ -0,0 +1,21 @@ +{{- if (and (not .Values.customTLSCertificate) (not .Values.certManager.enabled)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "nri-metadata-injection.fullname.admission" . }} + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + app: {{ include "nri-metadata-injection.name.admission" . }} + {{- include "newrelic.common.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "nri-metadata-injection.fullname.admission" . }} +subjects: + - kind: ServiceAccount + name: {{ include "nri-metadata-injection.fullname.admission.serviceAccount" . }} + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/serviceaccount.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/serviceaccount.yaml new file mode 100644 index 0000000000..027a59089a --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/job-patch/serviceaccount.yaml @@ -0,0 +1,14 @@ +{{- $createServiceAccount := include "newrelic.common.serviceAccount.create" . -}} +{{- if (and $createServiceAccount (not .Values.customTLSCertificate) (not .Values.certManager.enabled)) -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "nri-metadata-injection.fullname.admission.serviceAccount" . }} + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + app: {{ include "nri-metadata-injection.name.admission" . }} + {{- include "newrelic.common.labels" . | nindent 4 }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/mutatingWebhookConfiguration.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/mutatingWebhookConfiguration.yaml new file mode 100644 index 0000000000..b196d4f593 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/admission-webhooks/mutatingWebhookConfiguration.yaml @@ -0,0 +1,36 @@ +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + name: {{ include "newrelic.common.naming.fullname" . }} +{{- if .Values.certManager.enabled }} + annotations: + certmanager.k8s.io/inject-ca-from: {{ printf "%s/%s-root-cert" .Release.Namespace (include "newrelic.common.naming.fullname" .) | quote }} + cert-manager.io/inject-ca-from: {{ printf "%s/%s-root-cert" .Release.Namespace (include "newrelic.common.naming.fullname" .) | quote }} +{{- end }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +webhooks: +- name: metadata-injection.newrelic.com + clientConfig: + service: + name: {{ include "newrelic.common.naming.fullname" . }} + namespace: {{ .Release.Namespace }} + path: "/mutate" +{{- if not .Values.certManager.enabled }} + caBundle: "" +{{- end }} + rules: + - operations: ["CREATE"] + apiGroups: [""] + apiVersions: ["v1"] + resources: ["pods"] +{{- if .Values.injectOnlyLabeledNamespaces }} + scope: Namespaced + namespaceSelector: + matchLabels: + newrelic-metadata-injection: enabled +{{- end }} + failurePolicy: Ignore + timeoutSeconds: {{ .Values.timeoutSeconds }} + sideEffects: None + admissionReviewVersions: ["v1", "v1beta1"] diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/cert-manager.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/cert-manager.yaml new file mode 100644 index 0000000000..502fa44bb2 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/cert-manager.yaml @@ -0,0 +1,53 @@ +{{ if .Values.certManager.enabled }} +--- +# Create a selfsigned Issuer, in order to create a root CA certificate for +# signing webhook serving certificates +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: {{ include "nri-metadata-injection.fullname.self-signed-issuer" . }} + namespace: {{ .Release.Namespace }} +spec: + selfSigned: {} +--- +# Generate a CA Certificate used to sign certificates for the webhook +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: {{ include "newrelic.common.naming.fullname" . }}-root-cert + namespace: {{ .Release.Namespace }} +spec: + secretName: {{ include "newrelic.common.naming.fullname" . }}-root-cert + duration: {{ .Values.certManager.rootCertificateDuration}} + issuerRef: + name: {{ include "nri-metadata-injection.fullname.self-signed-issuer" . }} + commonName: "ca.webhook.nri" + isCA: true +--- +# Create an Issuer that uses the above generated CA certificate to issue certs +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: {{ include "nri-metadata-injection.fullname.root-issuer" . }} + namespace: {{ .Release.Namespace }} +spec: + ca: + secretName: {{ include "newrelic.common.naming.fullname" . }}-root-cert +--- + +# Finally, generate a serving certificate for the webhook to use +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: {{ include "nri-metadata-injection.fullname.webhook-cert" . }} + namespace: {{ .Release.Namespace }} +spec: + secretName: {{ include "nri-metadata-injection.fullname.admission" . }} + duration: {{ .Values.certManager.webhookCertificateDuration }} + issuerRef: + name: {{ include "nri-metadata-injection.fullname.root-issuer" . }} + dnsNames: + - {{ include "newrelic.common.naming.fullname" . }} + - {{ include "newrelic.common.naming.fullname" . }}.{{ .Release.Namespace }} + - {{ include "newrelic.common.naming.fullname" . }}.{{ .Release.Namespace }}.svc +{{ end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/deployment.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/deployment.yaml new file mode 100644 index 0000000000..4974dbbc1c --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/deployment.yaml @@ -0,0 +1,85 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "newrelic.common.naming.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicas }} + selector: + matchLabels: + {{- /* We cannot use the common library here because of a legacy issue */}} + {{- /* `selector` is immutable and the previous chart did not have all the idiomatic labels */}} + app.kubernetes.io/name: {{ include "newrelic.common.naming.name" . }} + template: + metadata: + {{- if .Values.podAnnotations }} + annotations: + {{- toYaml .Values.podAnnotations | nindent 8 }} + {{- end }} + labels: + {{- include "newrelic.common.labels.podLabels" . | nindent 8 }} + spec: + {{- with include "nri-metadata-injection.securityContext.pod" . }} + securityContext: + {{- . | nindent 8 -}} + {{- end }} + {{- with include "newrelic.common.priorityClassName" . }} + priorityClassName: {{ . }} + {{- end }} + {{- with include "newrelic.common.dnsConfig" . }} + dnsConfig: + {{- . | nindent 8 }} + {{- end }} + hostNetwork: {{ include "newrelic.common.hostNetwork.value" . }} + {{- if include "newrelic.common.hostNetwork" . }} + dnsPolicy: ClusterFirstWithHostNet + {{- end }} + + {{- with include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" ( list .Values.image.pullSecrets ) "context" .) }} + imagePullSecrets: + {{- . | nindent 8 -}} + {{- end }} + containers: + - name: {{ include "newrelic.common.naming.name" . }} + image: {{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.image "context" .) }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + {{- with include "newrelic.common.securityContext.container" . }} + securityContext: + {{- . | nindent 10 }} + {{- end }} + env: + - name: clusterName + value: {{ include "newrelic.common.cluster" . }} + ports: + - containerPort: 8443 + protocol: TCP + volumeMounts: + - name: tls-key-cert-pair + mountPath: /etc/tls-key-cert-pair + readinessProbe: + httpGet: + path: /health + port: 8080 + initialDelaySeconds: 1 + periodSeconds: 1 + {{- if .Values.resources }} + resources: + {{ toYaml .Values.resources | nindent 10 }} + {{- end }} + volumes: + - name: tls-key-cert-pair + secret: + secretName: {{ include "nri-metadata-injection.fullname.admission" . }} + nodeSelector: + kubernetes.io/os: linux + {{ include "newrelic.common.nodeSelector" . | nindent 8 }} + {{- with include "newrelic.common.tolerations" . }} + tolerations: + {{- . | nindent 8 -}} + {{- end }} + {{- with include "newrelic.common.affinity" . }} + affinity: + {{- . | nindent 8 -}} + {{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/service.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/service.yaml new file mode 100644 index 0000000000..e4a57587c6 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/templates/service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "newrelic.common.naming.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +spec: + ports: + - port: 443 + targetPort: 8443 + selector: + {{- include "newrelic.common.labels.selectorLabels" . | nindent 4 }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/tests/cluster_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/tests/cluster_test.yaml new file mode 100644 index 0000000000..a28487a06e --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/tests/cluster_test.yaml @@ -0,0 +1,39 @@ +suite: test cluster environment variable setup +templates: + - templates/deployment.yaml +release: + name: release + namespace: ns +tests: + - it: clusterName env is properly set + set: + cluster: my-cluster + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: clusterName + value: my-cluster + - it: fail when cluster is not defined + asserts: + - failedTemplate: + errorMessage: There is not cluster name definition set neither in `.global.cluster' nor `.cluster' in your values.yaml. Cluster name is required. + - it: has a linux node selector by default + set: + cluster: my-cluster + asserts: + - equal: + path: spec.template.spec.nodeSelector + value: + kubernetes.io/os: linux + - it: has a linux node selector and additional selectors + set: + cluster: my-cluster + nodeSelector: + aCoolTestLabel: aCoolTestValue + asserts: + - equal: + path: spec.template.spec.nodeSelector + value: + kubernetes.io/os: linux + aCoolTestLabel: aCoolTestValue diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/tests/job_serviceaccount_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/tests/job_serviceaccount_test.yaml new file mode 100644 index 0000000000..63b6f0534b --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/tests/job_serviceaccount_test.yaml @@ -0,0 +1,59 @@ +suite: test job' serviceAccount +templates: + - templates/admission-webhooks/job-patch/job-createSecret.yaml + - templates/admission-webhooks/job-patch/job-patchWebhook.yaml +release: + name: my-release + namespace: my-namespace +tests: + - it: RBAC points to the service account that is created by default + set: + cluster: test-cluster + rbac.create: true + serviceAccount.create: true + asserts: + - equal: + path: spec.template.spec.serviceAccountName + value: my-release-nri-metadata-injection-admission + + - it: RBAC points to the service account the user supplies when serviceAccount is disabled + set: + cluster: test-cluster + rbac.create: true + serviceAccount.create: false + serviceAccount.name: sa-test + asserts: + - equal: + path: spec.template.spec.serviceAccountName + value: sa-test + + - it: RBAC points to the service account the user supplies when serviceAccount is disabled + set: + cluster: test-cluster + rbac.create: true + serviceAccount.create: false + asserts: + - equal: + path: spec.template.spec.serviceAccountName + value: default + + - it: has a linux node selector by default + set: + cluster: my-cluster + asserts: + - equal: + path: spec.template.spec.nodeSelector + value: + kubernetes.io/os: linux + + - it: has a linux node selector and additional selectors + set: + cluster: my-cluster + nodeSelector: + aCoolTestLabel: aCoolTestValue + asserts: + - equal: + path: spec.template.spec.nodeSelector + value: + kubernetes.io/os: linux + aCoolTestLabel: aCoolTestValue diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/tests/rbac_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/tests/rbac_test.yaml new file mode 100644 index 0000000000..5a69191df9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/tests/rbac_test.yaml @@ -0,0 +1,38 @@ +suite: test RBAC creation +templates: + - templates/admission-webhooks/job-patch/rolebinding.yaml + - templates/admission-webhooks/job-patch/clusterrolebinding.yaml +release: + name: my-release + namespace: my-namespace +tests: + - it: RBAC points to the service account that is created by default + set: + cluster: test-cluster + rbac.create: true + serviceAccount.create: true + asserts: + - equal: + path: subjects[0].name + value: my-release-nri-metadata-injection-admission + + - it: RBAC points to the service account the user supplies when serviceAccount is disabled + set: + cluster: test-cluster + rbac.create: true + serviceAccount.create: false + serviceAccount.name: sa-test + asserts: + - equal: + path: subjects[0].name + value: sa-test + + - it: RBAC points to the service account the user supplies when serviceAccount is disabled + set: + cluster: test-cluster + rbac.create: true + serviceAccount.create: false + asserts: + - equal: + path: subjects[0].name + value: default diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/tests/volume_mounts_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/tests/volume_mounts_test.yaml new file mode 100644 index 0000000000..4a3c1327dd --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/tests/volume_mounts_test.yaml @@ -0,0 +1,30 @@ +suite: check volume mounts is properly set +templates: + - templates/admission-webhooks/job-patch/job-createSecret.yaml + - templates/admission-webhooks/job-patch/job-patchWebhook.yaml +release: + name: release + namespace: ns +tests: + - it: clusterName env is properly set + set: + cluster: my-cluster + jobImage: + volumeMounts: + - name: test-volume + volumePath: /test-volume + volumes: + - name: test-volume-container + emptyDir: {} + + asserts: + - contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: test-volume + volumePath: /test-volume + - contains: + path: spec.template.spec.volumes + content: + name: test-volume-container + emptyDir: {} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/values.yaml new file mode 100644 index 0000000000..849135c357 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-metadata-injection/values.yaml @@ -0,0 +1,102 @@ +# -- Override the name of the chart +nameOverride: "" +# -- Override the full name of the release +fullnameOverride: "" + +# -- Name of the Kubernetes cluster monitored. Can be configured also with `global.cluster` +cluster: "" + +# -- Image for the New Relic Metadata Injector +# @default -- See `values.yaml` +image: + registry: + repository: newrelic/k8s-metadata-injection + tag: "" # Defaults to chart's appVersion + pullPolicy: IfNotPresent + # -- The secrets that are needed to pull images from a custom registry. + pullSecrets: [] + # - name: regsecret + +# -- Image for creating the needed certificates of this webhook to work +# @default -- See `values.yaml` +jobImage: + registry: # Defaults to registry.k8s.io + repository: ingress-nginx/kube-webhook-certgen + tag: v1.3.0 + pullPolicy: IfNotPresent + # -- The secrets that are needed to pull images from a custom registry. + pullSecrets: [] + # - name: regsecret + + # -- Volume mounts to add to the job, you might want to mount tmp if Pod Security Policies + # Enforce a read-only root. + volumeMounts: [] + # - name: tmp + # mountPath: /tmp + + # -- Volumes to add to the job container + volumes: [] + # - name: tmp + # emptyDir: {} + +rbac: + # rbac.pspEnabled -- Whether the chart should create Pod Security Policy objects. + pspEnabled: false + +replicas: 1 + +# -- Additional labels for chart objects. Can be configured also with `global.labels` +labels: {} +# -- Annotations to be added to all pods created by the integration. +podAnnotations: {} +# -- Additional labels for chart pods. Can be configured also with `global.podLabels` +podLabels: {} + +# -- Image for creating the needed certificates of this webhook to work +# @default -- 100m/30M -/80M +resources: + limits: + memory: 80M + requests: + cpu: 100m + memory: 30M + +# -- Sets pod's priorityClassName. Can be configured also with `global.priorityClassName` +priorityClassName: "" +# -- (bool) Sets pod's hostNetwork. Can be configured also with `global.hostNetwork` +# @default -- false +hostNetwork: +# -- Sets pod's dnsConfig. Can be configured also with `global.dnsConfig` +dnsConfig: {} +# -- Sets security context (at pod level). Can be configured also with `global.podSecurityContext` +podSecurityContext: {} +# -- Sets security context (at container level). Can be configured also with `global.containerSecurityContext` +containerSecurityContext: {} + +certManager: + # certManager.enabled -- Use cert manager for webhook certs + enabled: false + # -- Sets the root certificate duration. Defaults to 43800h (5 years). + rootCertificateDuration: 43800h + # -- Sets certificate duration. Defaults to 8760h (1 year). + webhookCertificateDuration: 8760h + +# -- Sets pod/node affinities. Can be configured also with `global.affinity` +affinity: {} +# -- Sets pod's node selector. Can be configured also with `global.nodeSelector` +nodeSelector: {} +# -- Sets pod's tolerations to node taints. Can be configured also with `global.tolerations` +tolerations: [] + +# -- Enable the metadata decoration only for pods living in namespaces labeled +# with 'newrelic-metadata-injection=enabled'. +injectOnlyLabeledNamespaces: false + +# -- Use custom tls certificates for the webhook, or let the chart handle it +# automatically. +# Ref: https://docs.newrelic.com/docs/integrations/kubernetes-integration/link-your-applications/link-your-applications-kubernetes#configure-injection +customTLSCertificate: false + +# -- Webhook timeout +# Ref: https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#timeouts +timeoutSeconds: 28 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/.helmignore b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/.helmignore new file mode 100644 index 0000000000..50af031725 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/.helmignore @@ -0,0 +1,22 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/Chart.lock b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/Chart.lock new file mode 100644 index 0000000000..13fed1c852 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: common-library + repository: https://helm-charts.newrelic.com + version: 1.2.0 +digest: sha256:fa87cb007564a39a72739a3e850a91d6b03c0fc27a1115deac042b3ef77b4142 +generated: "2024-07-15T13:04:29.3144+02:00" diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/Chart.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/Chart.yaml new file mode 100644 index 0000000000..9f4153dabe --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/Chart.yaml @@ -0,0 +1,29 @@ +apiVersion: v2 +appVersion: 2.21.4 +dependencies: +- name: common-library + repository: https://helm-charts.newrelic.com + version: 1.2.0 +description: A Helm chart to deploy the New Relic Prometheus OpenMetrics integration +home: https://docs.newrelic.com/docs/infrastructure/prometheus-integrations/install-configure-openmetrics/configure-prometheus-openmetrics-integrations/ +icon: https://newrelic.com/themes/custom/curio/assets/mediakit/new_relic_logo_vertical.svg +keywords: +- prometheus +- newrelic +- monitoring +maintainers: +- name: alvarocabanas + url: https://github.com/alvarocabanas +- name: sigilioso + url: https://github.com/sigilioso +- name: gsanchezgavier + url: https://github.com/gsanchezgavier +- name: kang-makes + url: https://github.com/kang-makes +- name: paologallinaharbur + url: https://github.com/paologallinaharbur +name: nri-prometheus +sources: +- https://github.com/newrelic/nri-prometheus +- https://github.com/newrelic/nri-prometheus/tree/main/charts/nri-prometheus +version: 2.1.18 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/README.md b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/README.md new file mode 100644 index 0000000000..0287b2b2a2 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/README.md @@ -0,0 +1,116 @@ +# nri-prometheus + +A Helm chart to deploy the New Relic Prometheus OpenMetrics integration + +**Homepage:** + +# Helm installation + +You can install this chart using [`nri-bundle`](https://github.com/newrelic/helm-charts/tree/master/charts/nri-bundle) located in the +[helm-charts repository](https://github.com/newrelic/helm-charts) or directly from this repository by adding this Helm repository: + +```shell +helm repo add nri-prometheus https://newrelic.github.io/nri-prometheus +helm upgrade --install newrelic-prometheus nri-prometheus/nri-prometheus -f your-custom-values.yaml +``` + +## Source Code + +* +* + +## Scraping services and endpoints + +When a service is labeled or annotated with `scrape_enabled_label` (defaults to `prometheus.io/scrape`), +`nri-prometheus` will attempt to hit the service directly, rather than the endpoints behind it. + +This is the default behavior for compatibility reasons, but is known to cause issues if more than one endpoint +is behind the service, as metric queries will be load-balanced as well leading to inaccurate histograms. + +In order to change this behaviour set `scrape_endpoints` to `true` and `scrape_services` to `false`. +This will instruct `nri-prometheus` to scrape the underlying endpoints, as Prometheus server does. + +Existing users that are switching to this behavior should note that, depending on the number of endpoints +behind the services in the cluster the load and the metrics reported by those, data ingestion might see +an increase when flipping this option. Resource requirements might also be impacted, again depending on the number of new targets. + +While it is technically possible to set both `scrape_services` and `scrape_endpoints` to true, we do no recommend +doing so as it will lead to redundant metrics being processed, + +## Values managed globally + +This chart implements the [New Relic's common Helm library](https://github.com/newrelic/helm-charts/tree/master/library/common-library) which +means that it honors a wide range of defaults and globals common to most New Relic Helm charts. + +Options that can be defined globally include `affinity`, `nodeSelector`, `tolerations`, `proxy` and others. The full list can be found at +[user's guide of the common library](https://github.com/newrelic/helm-charts/blob/master/library/common-library/README.md). + +## Chart particularities + +### Low data mode +See this snippet from the `values.yaml` file: +```yaml +global: + lowDataMode: false +lowDataMode: false +``` + +To reduce the amount ot metrics we send to New Relic, enabling the `lowDataMode` will add [these transformations](static/lowdatamodedefaults.yaml): +```yaml +transformations: + - description: "Low data mode defaults" + ignore_metrics: + # Ignore the following metrics. + # These metrics are already collected by the New Relic Kubernetes Integration. + - prefixes: + - kube_ + - container_ + - machine_ + - cadvisor_ +``` + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| affinity | object | `{}` | Sets pod/node affinities. Can be configured also with `global.affinity` | +| cluster | string | `""` | Name of the Kubernetes cluster monitored. Can be configured also with `global.cluster` | +| config | object | See `values.yaml` | Provides your own `config.yaml` for this integration. Ref: https://docs.newrelic.com/docs/infrastructure/prometheus-integrations/install-configure-openmetrics/configure-prometheus-openmetrics-integrations/#example-configuration-file | +| containerSecurityContext | object | `{}` | Sets security context (at container level). Can be configured also with `global.containerSecurityContext` | +| customSecretLicenseKey | string | `""` | In case you don't want to have the license key in you values, this allows you to point to which secret key is the license key located. Can be configured also with `global.customSecretLicenseKey` | +| customSecretName | string | `""` | In case you don't want to have the license key in you values, this allows you to point to a user created secret to get the key from there. Can be configured also with `global.customSecretName` | +| dnsConfig | object | `{}` | Sets pod's dnsConfig. Can be configured also with `global.dnsConfig` | +| fedramp.enabled | bool | false | Enables FedRAMP. Can be configured also with `global.fedramp.enabled` | +| fullnameOverride | string | `""` | Override the full name of the release | +| hostNetwork | bool | `false` | Sets pod's hostNetwork. Can be configured also with `global.hostNetwork` | +| image | object | See `values.yaml` | Image for the New Relic Kubernetes integration | +| image.pullSecrets | list | `[]` | The secrets that are needed to pull images from a custom registry. | +| labels | object | `{}` | Additional labels for chart objects. Can be configured also with `global.labels` | +| licenseKey | string | `""` | This set this license key to use. Can be configured also with `global.licenseKey` | +| lowDataMode | bool | false | Reduces number of metrics sent in order to reduce costs. Can be configured also with `global.lowDataMode` | +| nameOverride | string | `""` | Override the name of the chart | +| nodeSelector | object | `{}` | Sets pod's node selector. Can be configured also with `global.nodeSelector` | +| nrStaging | bool | false | Send the metrics to the staging backend. Requires a valid staging license key. Can be configured also with `global.nrStaging` | +| podAnnotations | object | `{}` | Annotations to be added to all pods created by the integration. | +| podLabels | object | `{}` | Additional labels for chart pods. Can be configured also with `global.podLabels` | +| podSecurityContext | object | `{}` | Sets security context (at pod level). Can be configured also with `global.podSecurityContext` | +| priorityClassName | string | `""` | Sets pod's priorityClassName. Can be configured also with `global.priorityClassName` | +| proxy | string | `""` | Configures the integration to send all HTTP/HTTPS request through the proxy in that URL. The URL should have a standard format like `https://user:password@hostname:port`. Can be configured also with `global.proxy` | +| rbac.create | bool | `true` | Specifies whether RBAC resources should be created | +| resources | object | `{}` | | +| serviceAccount.annotations | object | `{}` | Add these annotations to the service account we create. Can be configured also with `global.serviceAccount.annotations` | +| serviceAccount.create | bool | `true` | Configures if the service account should be created or not. Can be configured also with `global.serviceAccount.create` | +| serviceAccount.name | string | `nil` | Change the name of the service account. This is honored if you disable on this cahrt the creation of the service account so you can use your own. Can be configured also with `global.serviceAccount.name` | +| tolerations | list | `[]` | Sets pod's tolerations to node taints. Can be configured also with `global.tolerations` | +| verboseLog | bool | false | Sets the debug logs to this integration or all integrations if it is set globally. Can be configured also with `global.verboseLog` | + +## Maintainers + +* [alvarocabanas](https://github.com/alvarocabanas) +* [carlossscastro](https://github.com/carlossscastro) +* [sigilioso](https://github.com/sigilioso) +* [gsanchezgavier](https://github.com/gsanchezgavier) +* [kang-makes](https://github.com/kang-makes) +* [marcsanmi](https://github.com/marcsanmi) +* [paologallinaharbur](https://github.com/paologallinaharbur) +* [roobre](https://github.com/roobre) diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/README.md.gotmpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/README.md.gotmpl new file mode 100644 index 0000000000..5c1da45775 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/README.md.gotmpl @@ -0,0 +1,83 @@ +{{ template "chart.header" . }} +{{ template "chart.deprecationWarning" . }} + +{{ template "chart.description" . }} + +{{ template "chart.homepageLine" . }} + +# Helm installation + +You can install this chart using [`nri-bundle`](https://github.com/newrelic/helm-charts/tree/master/charts/nri-bundle) located in the +[helm-charts repository](https://github.com/newrelic/helm-charts) or directly from this repository by adding this Helm repository: + +```shell +helm repo add nri-prometheus https://newrelic.github.io/nri-prometheus +helm upgrade --install newrelic-prometheus nri-prometheus/nri-prometheus -f your-custom-values.yaml +``` + +{{ template "chart.sourcesSection" . }} + +## Scraping services and endpoints + +When a service is labeled or annotated with `scrape_enabled_label` (defaults to `prometheus.io/scrape`), +`nri-prometheus` will attempt to hit the service directly, rather than the endpoints behind it. + +This is the default behavior for compatibility reasons, but is known to cause issues if more than one endpoint +is behind the service, as metric queries will be load-balanced as well leading to inaccurate histograms. + +In order to change this behaviour set `scrape_endpoints` to `true` and `scrape_services` to `false`. +This will instruct `nri-prometheus` to scrape the underlying endpoints, as Prometheus server does. + +Existing users that are switching to this behavior should note that, depending on the number of endpoints +behind the services in the cluster the load and the metrics reported by those, data ingestion might see +an increase when flipping this option. Resource requirements might also be impacted, again depending on the number of new targets. + +While it is technically possible to set both `scrape_services` and `scrape_endpoints` to true, we do no recommend +doing so as it will lead to redundant metrics being processed, + +## Values managed globally + +This chart implements the [New Relic's common Helm library](https://github.com/newrelic/helm-charts/tree/master/library/common-library) which +means that it honors a wide range of defaults and globals common to most New Relic Helm charts. + +Options that can be defined globally include `affinity`, `nodeSelector`, `tolerations`, `proxy` and others. The full list can be found at +[user's guide of the common library](https://github.com/newrelic/helm-charts/blob/master/library/common-library/README.md). + +## Chart particularities + +### Low data mode +See this snippet from the `values.yaml` file: +```yaml +global: + lowDataMode: false +lowDataMode: false +``` + +To reduce the amount ot metrics we send to New Relic, enabling the `lowDataMode` will add [these transformations](static/lowdatamodedefaults.yaml): +```yaml +transformations: + - description: "Low data mode defaults" + ignore_metrics: + # Ignore the following metrics. + # These metrics are already collected by the New Relic Kubernetes Integration. + - prefixes: + - kube_ + - container_ + - machine_ + - cadvisor_ +``` + +{{ template "chart.valuesSection" . }} + +{{ if .Maintainers }} +## Maintainers +{{ range .Maintainers }} +{{- if .Name }} +{{- if .Url }} +* [{{ .Name }}]({{ .Url }}) +{{- else }} +* {{ .Name }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/.helmignore b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/.helmignore new file mode 100644 index 0000000000..0e8a0eb36f --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/.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 +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/Chart.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/Chart.yaml new file mode 100644 index 0000000000..b65ac15d4c --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/Chart.yaml @@ -0,0 +1,17 @@ +apiVersion: v2 +description: Provides helpers to provide consistency on all the charts +keywords: +- newrelic +- chart-library +maintainers: +- name: juanjjaramillo + url: https://github.com/juanjjaramillo +- name: csongnr + url: https://github.com/csongnr +- name: dbudziwojskiNR + url: https://github.com/dbudziwojskiNR +- name: kang-makes + url: https://github.com/kang-makes +name: common-library +type: library +version: 1.2.0 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/DEVELOPERS.md b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/DEVELOPERS.md new file mode 100644 index 0000000000..3ccc108e2d --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/DEVELOPERS.md @@ -0,0 +1,663 @@ +# Functions/templates documented for chart writers +Here is some rough documentation separated by the file that contains the function, the function +name and how to use it. We are not covering functions that start with `_` (e.g. +`newrelic.common.license._licenseKey`) because they are used internally by this library for +other helpers. Helm does not have the concept of "public" or "private" functions/templates so +this is a convention of ours. + +## _naming.tpl +These functions are used to name objects. + +### `newrelic.common.naming.name` +This is the same as the idiomatic `CHART-NAME.name` that is created when you use `helm create`. + +It honors `.Values.nameOverride`. + +Usage: +```mustache +{{ include "newrelic.common.naming.name" . }} +``` + +### `newrelic.common.naming.fullname` +This is the same as the idiomatic `CHART-NAME.fullname` that is created when you use `helm create` + +It honors `.Values.fullnameOverride`. + +Usage: +```mustache +{{ include "newrelic.common.naming.fullname" . }} +``` + +### `newrelic.common.naming.chart` +This is the same as the idiomatic `CHART-NAME.chart` that is created when you use `helm create`. + +It is mostly useless for chart writers. It is used internally for templating the labels but there +is no reason to keep it "private". + +Usage: +```mustache +{{ include "newrelic.common.naming.chart" . }} +``` + +### `newrelic.common.naming.truncateToDNS` +This is a useful template that could be used to trim a string to 63 chars and does not end with a dash (`-`). +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). + +Usage: +```mustache +{{ $nameToTruncate := "a-really-really-really-really-REALLY-long-string-that-should-be-truncated-because-it-is-enought-long-to-brak-something" +{{- $truncatedName := include "newrelic.common.naming.truncateToDNS" $nameToTruncate }} +{{- $truncatedName }} +{{- /* This should print: a-really-really-really-really-REALLY-long-string-that-should-be */ -}} +``` + +### `newrelic.common.naming.truncateToDNSWithSuffix` +This template function is the same as the above but instead of receiving a string you should give a `dict` +with a `name` and a `suffix`. This function will join them with a dash (`-`) and trim the `name` so the +result of `name-suffix` is no more than 63 chars + +Usage: +```mustache +{{ $nameToTruncate := "a-really-really-really-really-REALLY-long-string-that-should-be-truncated-because-it-is-enought-long-to-brak-something" +{{- $suffix := "A-NOT-SO-LONG-SUFFIX" }} +{{- $truncatedName := include "truncateToDNSWithSuffix" (dict "name" $nameToTruncate "suffix" $suffix) }} +{{- $truncatedName }} +{{- /* This should print: a-really-really-really-really-REALLY-long-A-NOT-SO-LONG-SUFFIX */ -}} +``` + + + +## _labels.tpl +### `newrelic.common.labels`, `newrelic.common.labels.selectorLabels` and `newrelic.common.labels.podLabels` +These are functions that are used to label objects. They are configured by this `values.yaml` +```yaml +global: + podLabels: {} # included in all the pods of all the charts that implement this library + labels: {} # included in all the objects of all the charts that implement this library +podLabels: {} # included in all the pods of this chart +labels: {} # included in all the objects of this chart +``` + +label maps are merged from global to local values. + +And chart writer should use them like this: +```mustache +metadata: + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +spec: + selector: + matchLabels: + {{- include "newrelic.common.labels.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "newrelic.common.labels.podLabels" . | nindent 8 }} +``` + +`newrelic.common.labels.podLabels` includes `newrelic.common.labels.selectorLabels` automatically. + + + +## _priority-class-name.tpl +### `newrelic.common.priorityClassName` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + priorityClassName: "" +priorityClassName: "" +``` + +Be careful: chart writers should put an empty string (or any kind of Helm falsiness) for this +library to work properly. If in your values a non-falsy `priorityClassName` is found, the global +one is going to be always ignored. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.priorityClassName" . }} + priorityClassName: {{ . }} + {{- end }} +``` + + + +## _hostnetwork.tpl +### `newrelic.common.hostNetwork` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + hostNetwork: # Note that this is empty (nil) +hostNetwork: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `hostNetwork` is defined, the global one is going to be always ignored. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.hostNetwork" . }} + hostNetwork: {{ . }} + {{- end }} +``` + +### `newrelic.common.hostNetwork.value` +This function is an abstraction of the function above but this returns directly "true" or "false". + +Be careful with using this with an `if` as Helm does evaluate "false" (string) as `true`. + +Usage (example in a pod spec): +```mustache +spec: + hostNetwork: {{ include "newrelic.common.hostNetwork.value" . }} +``` + + + +## _dnsconfig.tpl +### `newrelic.common.dnsConfig` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + dnsConfig: {} +dnsConfig: {} +``` + +Be careful: chart writers should put an empty string (or any kind of Helm falsiness) for this +library to work properly. If in your values a non-falsy `dnsConfig` is found, the global +one is going to be always ignored. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.dnsConfig" . }} + dnsConfig: + {{- . | nindent 4 }} + {{- end }} +``` + + + +## _images.tpl +These functions help us to deal with how images are templated. This allows setting `registries` +where to fetch images globally while being flexible enough to fit in different maps of images +and deployments with one or more images. This is the example of a complex `values.yaml` that +we are going to use during the documentation of these functions: + +```yaml +global: + images: + registry: nexus-3-instance.internal.clients-domain.tld +jobImage: + registry: # defaults to "example.tld" when empty in these examples + repository: ingress-nginx/kube-webhook-certgen + tag: v1.1.1 + pullPolicy: IfNotPresent + pullSecrets: [] +images: + integration: + registry: + repository: newrelic/nri-kube-events + tag: 1.8.0 + pullPolicy: IfNotPresent + agent: + registry: + repository: newrelic/k8s-events-forwarder + tag: 1.22.0 + pullPolicy: IfNotPresent + pullSecrets: [] +``` + +### `newrelic.common.images.image` +This will return a string with the image ready to be downloaded that includes the registry, the image and the tag. +`defaultRegistry` is used to keep `registry` field empty in `values.yaml` so you can override the image using +`global.images.registry`, your local `jobImage.registry` and be able to fallback to a registry that is not `docker.io` +(Or the default repository that the client could have set in the CRI). + +Usage: +```mustache +{{- /* For the integration */}} +{{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.images.agent "context" .) }} +{{- /* For jobImage */}} +{{ include "newrelic.common.images.image" ( dict "defaultRegistry" "example.tld" "imageRoot" .Values.jobImage "context" .) }} +``` + +### `newrelic.common.images.registry` +It returns the registry from the global or local values. You should avoid using this helper to create your image +URL and use `newrelic.common.images.image` instead, but it is there to be used in case it is needed. + +Usage: +```mustache +{{- /* For the integration */}} +{{ include "newrelic.common.images.registry" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.registry" ( dict "imageRoot" .Values.images.agent "context" .) }} +{{- /* For jobImage */}} +{{ include "newrelic.common.images.registry" ( dict "defaultRegistry" "example.tld" "imageRoot" .Values.jobImage "context" .) }} +``` + +### `newrelic.common.images.repository` +It returns the image from the values. You should avoid using this helper to create your image +URL and use `newrelic.common.images.image` instead, but it is there to be used in case it is needed. + +Usage: +```mustache +{{- /* For jobImage */}} +{{ include "newrelic.common.images.repository" ( dict "imageRoot" .Values.jobImage "context" .) }} +{{- /* For the integration */}} +{{ include "newrelic.common.images.repository" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.repository" ( dict "imageRoot" .Values.images.agent "context" .) }} +``` + +### `newrelic.common.images.tag` +It returns the image's tag from the values. You should avoid using this helper to create your image +URL and use `newrelic.common.images.image` instead, but it is there to be used in case it is needed. + +Usage: +```mustache +{{- /* For jobImage */}} +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.jobImage "context" .) }} +{{- /* For the integration */}} +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.images.integration "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.images.agent "context" .) }} +``` + +### `newrelic.common.images.renderPullSecrets` +If returns a merged map that contains the pull secrets from the global configuration and the local one. + +Usage: +```mustache +{{- /* For jobImage */}} +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" .Values.jobImage.pullSecrets "context" .) }} +{{- /* For the integration */}} +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" .Values.images.pullSecrets "context" .) }} +{{- /* For the agent */}} +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" .Values.images.pullSecrets "context" .) }} +``` + + + +## _serviceaccount.tpl +These functions are used to evaluate if the service account should be created, with which name and add annotations to it. + +The functions that the common library has implemented for service accounts are: +* `newrelic.common.serviceAccount.create` +* `newrelic.common.serviceAccount.name` +* `newrelic.common.serviceAccount.annotations` + +Usage: +```mustache +{{- if include "newrelic.common.serviceAccount.create" . -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + {{- with (include "newrelic.common.serviceAccount.annotations" .) }} + annotations: + {{- . | nindent 4 }} + {{- end }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include "newrelic.common.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} +{{- end }} +``` + + + +## _affinity.tpl, _nodeselector.tpl and _tolerations.tpl +These three files are almost the same and they follow the idiomatic way of `helm create`. + +Each function also looks if there is a global value like the other helpers. +```yaml +global: + affinity: {} + nodeSelector: {} + tolerations: [] +affinity: {} +nodeSelector: {} +tolerations: [] +``` + +The values here are replaced instead of be merged. If a value at root level is found, the global one is ignored. + +Usage (example in a pod spec): +```mustache +spec: + {{- with include "newrelic.common.nodeSelector" . }} + nodeSelector: + {{- . | nindent 4 }} + {{- end }} + {{- with include "newrelic.common.affinity" . }} + affinity: + {{- . | nindent 4 }} + {{- end }} + {{- with include "newrelic.common.tolerations" . }} + tolerations: + {{- . | nindent 4 }} + {{- end }} +``` + + + +## _agent-config.tpl +### `newrelic.common.agentConfig.defaults` +This returns a YAML that the agent can use directly as a config that includes other options from the values file like verbose mode, +custom attributes, FedRAMP and such. + +Usage: +```mustache +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + name: {{ include newrelic.common.naming.truncateToDNSWithSuffix (dict "name" (include "newrelic.common.naming.fullname" .) suffix "agent-config") }} + namespace: {{ .Release.Namespace }} +data: + newrelic-infra.yml: |- + # This is the configuration file for the infrastructure agent. See: + # https://docs.newrelic.com/docs/infrastructure/install-infrastructure-agent/configuration/infrastructure-agent-configuration-settings/ + {{- include "newrelic.common.agentConfig.defaults" . | nindent 4 }} +``` + + + +## _cluster.tpl +### `newrelic.common.cluster` +Returns the cluster name + +Usage: +```mustache +{{ include "newrelic.common.cluster" . }} +``` + + + +## _custom-attributes.tpl +### `newrelic.common.customAttributes` +Return custom attributes in YAML format. + +Usage: +```mustache +apiVersion: v1 +kind: ConfigMap +metadata: + name: example +data: + custom-attributes.yaml: | + {{- include "newrelic.common.customAttributes" . | nindent 4 }} + custom-attributes.json: | + {{- include "newrelic.common.customAttributes" . | fromYaml | toJson | nindent 4 }} +``` + + + +## _fedramp.tpl +### `newrelic.common.fedramp.enabled` +Returns true if FedRAMP is enabled or an empty string if not. It can be safely used in conditionals as an empty string is a Helm falsiness. + +Usage: +```mustache +{{ include "newrelic.common.fedramp.enabled" . }} +``` + +### `newrelic.common.fedramp.enabled.value` +Returns true if FedRAMP is enabled or false if not. This is to have the value of FedRAMP ready to be templated. + +Usage: +```mustache +{{ include "newrelic.common.fedramp.enabled.value" . }} +``` + + + +## _license.tpl +### `newrelic.common.license.secretName` and ### `newrelic.common.license.secretKeyName` +Returns the secret and key inside the secret where to read the license key. + +The common library will take care of using a user-provided custom secret or creating a secret that contains the license key. + +To create the secret use `newrelic.common.license.secret`. + +Usage: +```mustache +{{- if and (.Values.controlPlane.enabled) (not (include "newrelic.fargate" .)) }} +apiVersion: v1 +kind: Pod +metadata: + name: example +spec: + containers: + - name: agent + env: + - name: "NRIA_LICENSE_KEY" + valueFrom: + secretKeyRef: + name: {{ include "newrelic.common.license.secretName" . }} + key: {{ include "newrelic.common.license.secretKeyName" . }} +``` + + + +## _license_secret.tpl +### `newrelic.common.license.secret` +This function templates the secret that is used by agents and integrations with the license Key provided by the user. It will +template nothing (empty string) if the user provides a custom pair of secret name and key. + +This template also fails in case the user has not provided any license key or custom secret so no safety checks have to be done +by chart writers. + +You just must have a template with these two lines: +```mustache +{{- /* Common library will take care of creating the secret or not. */ -}} +{{- include "newrelic.common.license.secret" . -}} +``` + + + +## _insights.tpl +### `newrelic.common.insightsKey.secretName` and ### `newrelic.common.insightsKey.secretKeyName` +Returns the secret and key inside the secret where to read the insights key. + +The common library will take care of using a user-provided custom secret or creating a secret that contains the insights key. + +To create the secret use `newrelic.common.insightsKey.secret`. + +Usage: +```mustache +apiVersion: v1 +kind: Pod +metadata: + name: statsd +spec: + containers: + - name: statsd + env: + - name: "INSIGHTS_KEY" + valueFrom: + secretKeyRef: + name: {{ include "newrelic.common.insightsKey.secretName" . }} + key: {{ include "newrelic.common.insightsKey.secretKeyName" . }} +``` + + + +## _insights_secret.tpl +### `newrelic.common.insightsKey.secret` +This function templates the secret that is used by agents and integrations with the insights key provided by the user. It will +template nothing (empty string) if the user provides a custom pair of secret name and key. + +This template also fails in case the user has not provided any insights key or custom secret so no safety checks have to be done +by chart writers. + +You just must have a template with these two lines: +```mustache +{{- /* Common library will take care of creating the secret or not. */ -}} +{{- include "newrelic.common.insightsKey.secret" . -}} +``` + + + +## _low-data-mode.tpl +### `newrelic.common.lowDataMode` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + lowDataMode: # Note that this is empty (nil) +lowDataMode: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `lowdataMode` is defined, the global one is going to be always ignored. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage: +```mustache +{{ include "newrelic.common.lowDataMode" . }} +``` + + + +## _privileged.tpl +### `newrelic.common.privileged` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + privileged: # Note that this is empty (nil) +privileged: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `privileged` is defined, the global one is going to be always ignored. + +Chart writers could override this and put directly a `true` in the `values.yaml` to override the +default of the common library. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage: +```mustache +{{ include "newrelic.common.privileged" . }} +``` + +### `newrelic.common.privileged.value` +Returns true if privileged mode is enabled or false if not. This is to have the value of privileged ready to be templated. + +Usage: +```mustache +{{ include "newrelic.common.privileged.value" . }} +``` + + + +## _proxy.tpl +### `newrelic.common.proxy` +Returns the proxy URL configured by the user. + +Usage: +```mustache +{{ include "newrelic.common.proxy" . }} +``` + + + +## _security-context.tpl +Use these functions to share the security context among all charts. Useful in clusters that have security enforcing not to +use the root user (like OpenShift) or users that have an admission webhooks. + +The functions are: +* `newrelic.common.securityContext.container` +* `newrelic.common.securityContext.pod` + +Usage: +```mustache +apiVersion: v1 +kind: Pod +metadata: + name: example +spec: + spec: + {{- with include "newrelic.common.securityContext.pod" . }} + securityContext: + {{- . | nindent 8 }} + {{- end }} + + containers: + - name: example + {{- with include "nriKubernetes.securityContext.container" . }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} +``` + + + +## _staging.tpl +### `newrelic.common.nrStaging` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + nrStaging: # Note that this is empty (nil) +nrStaging: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `nrStaging` is defined, the global one is going to be always ignored. + +This function returns "true" of "" (empty string) so it can be used for evaluating conditionals. + +Usage: +```mustache +{{ include "newrelic.common.nrStaging" . }} +``` + +### `newrelic.common.nrStaging.value` +Returns true if staging is enabled or false if not. This is to have the staging value ready to be templated. + +Usage: +```mustache +{{ include "newrelic.common.nrStaging.value" . }} +``` + + + +## _verbose-log.tpl +### `newrelic.common.verboseLog` +Like almost everything in this library, it reads global and local variables: +```yaml +global: + verboseLog: # Note that this is empty (nil) +verboseLog: # Note that this is empty (nil) +``` + +Be careful: chart writers should NOT PUT ANY VALUE for this library to work properly. If in you +values a `verboseLog` is defined, the global one is going to be always ignored. + +Usage: +```mustache +{{ include "newrelic.common.verboseLog" . }} +``` + +### `newrelic.common.verboseLog.valueAsBoolean` +Returns true if verbose is enabled or false if not. This is to have the verbose value ready to be templated as a boolean + +Usage: +```mustache +{{ include "newrelic.common.verboseLog.valueAsBoolean" . }} +``` + +### `newrelic.common.verboseLog.valueAsInt` +Returns 1 if verbose is enabled or 0 if not. This is to have the verbose value ready to be templated as an integer + +Usage: +```mustache +{{ include "newrelic.common.verboseLog.valueAsInt" . }} +``` diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/README.md b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/README.md new file mode 100644 index 0000000000..10f08ca677 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/README.md @@ -0,0 +1,106 @@ +# Helm Common library + +The common library is a way to unify the UX through all the Helm charts that implement it. + +The tooling suite that New Relic is huge and growing and this allows to set things globally +and locally for a single chart. + +## Documentation for chart writers + +If you are writing a chart that is going to use this library you can check the [developers guide](/library/common-library/DEVELOPERS.md) to see all +the functions/templates that we have implemented, what they do and how to use them. + +## Values managed globally + +We want to have a seamless experience through all the charts so we created this library that tries to standardize the behaviour +of all the charts. Sadly, because of the complexity of all these integrations, not all the charts behave exactly as expected. + +An example is `newrelic-infrastructure` that ignores `hostNetwork` in the control plane scraper because most of the users has the +control plane listening in the node to `localhost`. + +For each chart that has a special behavior (or further information of the behavior) there is a "chart particularities" section +in its README.md that explains which is the expected behavior. + +At the time of writing this, all the charts from `nri-bundle` except `newrelic-logging` and `synthetics-minion` implements this +library and honors global options as described in this document. + +Here is a list of global options: + +| Global keys | Local keys | Default | Merged[1](#values-managed-globally-1) | Description | +|-------------|------------|---------|--------------------------------------------------|-------------| +| global.cluster | cluster | `""` | | Name of the Kubernetes cluster monitored | +| global.licenseKey | licenseKey | `""` | | This set this license key to use | +| global.customSecretName | customSecretName | `""` | | In case you don't want to have the license key in you values, this allows you to point to a user created secret to get the key from there | +| global.customSecretLicenseKey | customSecretLicenseKey | `""` | | In case you don't want to have the license key in you values, this allows you to point to which secret key is the license key located | +| global.podLabels | podLabels | `{}` | yes | Additional labels for chart pods | +| global.labels | labels | `{}` | yes | Additional labels for chart objects | +| global.priorityClassName | priorityClassName | `""` | | Sets pod's priorityClassName | +| global.hostNetwork | hostNetwork | `false` | | Sets pod's hostNetwork | +| global.dnsConfig | dnsConfig | `{}` | | Sets pod's dnsConfig | +| global.images.registry | See [Further information](#values-managed-globally-2) | `""` | | Changes the registry where to get the images. Useful when there is an internal image cache/proxy | +| global.images.pullSecrets | See [Further information](#values-managed-globally-2) | `[]` | yes | Set secrets to be able to fetch images | +| global.podSecurityContext | podSecurityContext | `{}` | | Sets security context (at pod level) | +| global.containerSecurityContext | containerSecurityContext | `{}` | | Sets security context (at container level) | +| global.affinity | affinity | `{}` | | Sets pod/node affinities | +| global.nodeSelector | nodeSelector | `{}` | | Sets pod's node selector | +| global.tolerations | tolerations | `[]` | | Sets pod's tolerations to node taints | +| global.serviceAccount.create | serviceAccount.create | `true` | | Configures if the service account should be created or not | +| global.serviceAccount.name | serviceAccount.name | name of the release | | Change the name of the service account. This is honored if you disable on this cahrt the creation of the service account so you can use your own. | +| global.serviceAccount.annotations | serviceAccount.annotations | `{}` | yes | Add these annotations to the service account we create | +| global.customAttributes | customAttributes | `{}` | | Adds extra attributes to the cluster and all the metrics emitted to the backend | +| global.fedramp | fedramp | `false` | | Enables FedRAMP | +| global.lowDataMode | lowDataMode | `false` | | Reduces number of metrics sent in order to reduce costs | +| global.privileged | privileged | Depends on the chart | | In each integration it has different behavior. See [Further information](#values-managed-globally-3) but all aims to send less metrics to the backend to try to save costs | +| global.proxy | proxy | `""` | | Configures the integration to send all HTTP/HTTPS request through the proxy in that URL. The URL should have a standard format like `https://user:password@hostname:port` | +| global.nrStaging | nrStaging | `false` | | Send the metrics to the staging backend. Requires a valid staging license key | +| global.verboseLog | verboseLog | `false` | | Sets the debug/trace logs to this integration or all integrations if it is set globally | + +### Further information + +#### 1. Merged + +Merged means that the values from global are not replaced by the local ones. Think in this example: +```yaml +global: + labels: + global: global + hostNetwork: true + nodeSelector: + global: global + +labels: + local: local +nodeSelector: + local: local +hostNetwork: false +``` + +This values will template `hostNetwork` to `false`, a map of labels `{ "global": "global", "local": "local" }` and a `nodeSelector` with +`{ "local": "local" }`. + +As Helm by default merges all the maps it could be confusing that we have two behaviors (merging `labels` and replacing `nodeSelector`) +the `values` from global to local. This is the rationale behind this: +* `hostNetwork` is templated to `false` because is overriding the value defined globally. +* `labels` are merged because the user may want to label all the New Relic pods at once and label other solution pods differently for + clarity' sake. +* `nodeSelector` does not merge as `labels` because could make it harder to overwrite/delete a selector that comes from global because + of the logic that Helm follows merging maps. + + +#### 2. Fine grain registries + +Some charts only have 1 image while others that can have 2 or more images. The local path for the registry can change depending +on the chart itself. + +As this is mostly unique per helm chart, you should take a look to the chart's values table (or directly to the `values.yaml` file to see all the +images that you can change. + +This should only be needed if you have an advanced setup that forces you to have granularity enough to force a proxy/cache registry per integration. + + + +#### 3. Privileged mode + +By default, from the common library, the privileged mode is set to false. But most of the helm charts require this to be true to fetch more +metrics so could see a true in some charts. The consequences of the privileged mode differ from one chart to another so for each chart that +honors the privileged mode toggle should be a section in the README explaining which is the behavior with it enabled or disabled. diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_affinity.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_affinity.tpl new file mode 100644 index 0000000000..1b2636754e --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_affinity.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod affinity */ -}} +{{- define "newrelic.common.affinity" -}} + {{- if .Values.affinity -}} + {{- toYaml .Values.affinity -}} + {{- else if .Values.global -}} + {{- if .Values.global.affinity -}} + {{- toYaml .Values.global.affinity -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_agent-config.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_agent-config.tpl new file mode 100644 index 0000000000..9c32861a02 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_agent-config.tpl @@ -0,0 +1,26 @@ +{{/* +This helper should return the defaults that all agents should have +*/}} +{{- define "newrelic.common.agentConfig.defaults" -}} +{{- if include "newrelic.common.verboseLog" . }} +log: + level: trace +{{- end }} + +{{- if (include "newrelic.common.nrStaging" . ) }} +staging: true +{{- end }} + +{{- with include "newrelic.common.proxy" . }} +proxy: {{ . | quote }} +{{- end }} + +{{- with include "newrelic.common.fedramp.enabled" . }} +fedramp: {{ . }} +{{- end }} + +{{- with fromYaml ( include "newrelic.common.customAttributes" . ) }} +custom_attributes: + {{- toYaml . | nindent 2 }} +{{- end }} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_cluster.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_cluster.tpl new file mode 100644 index 0000000000..0197dd35a3 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_cluster.tpl @@ -0,0 +1,15 @@ +{{/* +Return the cluster +*/}} +{{- define "newrelic.common.cluster" -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} + +{{- if .Values.cluster -}} + {{- .Values.cluster -}} +{{- else if $global.cluster -}} + {{- $global.cluster -}} +{{- else -}} + {{ fail "There is not cluster name definition set neither in `.global.cluster' nor `.cluster' in your values.yaml. Cluster name is required." }} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_custom-attributes.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_custom-attributes.tpl new file mode 100644 index 0000000000..92020719c3 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_custom-attributes.tpl @@ -0,0 +1,17 @@ +{{/* +This will render custom attributes as a YAML ready to be templated or be used with `fromYaml`. +*/}} +{{- define "newrelic.common.customAttributes" -}} +{{- $customAttributes := dict -}} + +{{- $global := index .Values "global" | default dict -}} +{{- if $global.customAttributes -}} +{{- $customAttributes = mergeOverwrite $customAttributes $global.customAttributes -}} +{{- end -}} + +{{- if .Values.customAttributes -}} +{{- $customAttributes = mergeOverwrite $customAttributes .Values.customAttributes -}} +{{- end -}} + +{{- toYaml $customAttributes -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_dnsconfig.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_dnsconfig.tpl new file mode 100644 index 0000000000..d4e40aa8af --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_dnsconfig.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod dnsConfig */ -}} +{{- define "newrelic.common.dnsConfig" -}} + {{- if .Values.dnsConfig -}} + {{- toYaml .Values.dnsConfig -}} + {{- else if .Values.global -}} + {{- if .Values.global.dnsConfig -}} + {{- toYaml .Values.global.dnsConfig -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_fedramp.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_fedramp.tpl new file mode 100644 index 0000000000..9df8d6b5e9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_fedramp.tpl @@ -0,0 +1,25 @@ +{{- /* Defines the fedRAMP flag */ -}} +{{- define "newrelic.common.fedramp.enabled" -}} + {{- if .Values.fedramp -}} + {{- if .Values.fedramp.enabled -}} + {{- .Values.fedramp.enabled -}} + {{- end -}} + {{- else if .Values.global -}} + {{- if .Values.global.fedramp -}} + {{- if .Values.global.fedramp.enabled -}} + {{- .Values.global.fedramp.enabled -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} + + + +{{- /* Return FedRAMP value directly ready to be templated */ -}} +{{- define "newrelic.common.fedramp.enabled.value" -}} +{{- if include "newrelic.common.fedramp.enabled" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_hostnetwork.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_hostnetwork.tpl new file mode 100644 index 0000000000..4cf017ef7e --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_hostnetwork.tpl @@ -0,0 +1,39 @@ +{{- /* +Abstraction of the hostNetwork toggle. +This helper allows to override the global `.global.hostNetwork` with the value of `.hostNetwork`. +Returns "true" if `hostNetwork` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.hostNetwork" -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} + +{{- /* +`get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs + +We also want only to return when this is true, returning `false` here will template "false" (string) when doing +an `(include "newrelic.common.hostNetwork" .)`, which is not an "empty string" so it is `true` if it is used +as an evaluation somewhere else. +*/ -}} +{{- if get .Values "hostNetwork" | kindIs "bool" -}} + {{- if .Values.hostNetwork -}} + {{- .Values.hostNetwork -}} + {{- end -}} +{{- else if get $global "hostNetwork" | kindIs "bool" -}} + {{- if $global.hostNetwork -}} + {{- $global.hostNetwork -}} + {{- end -}} +{{- end -}} +{{- end -}} + + +{{- /* +Abstraction of the hostNetwork toggle. +This helper abstracts the function "newrelic.common.hostNetwork" to return true or false directly. +*/ -}} +{{- define "newrelic.common.hostNetwork.value" -}} +{{- if include "newrelic.common.hostNetwork" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_images.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_images.tpl new file mode 100644 index 0000000000..d4fb432905 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_images.tpl @@ -0,0 +1,94 @@ +{{- /* +Return the proper image name +{{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.path.to.the.image "defaultRegistry" "your.private.registry.tld" "context" .) }} +*/ -}} +{{- define "newrelic.common.images.image" -}} + {{- $registryName := include "newrelic.common.images.registry" ( dict "imageRoot" .imageRoot "defaultRegistry" .defaultRegistry "context" .context ) -}} + {{- $repositoryName := include "newrelic.common.images.repository" .imageRoot -}} + {{- $tag := include "newrelic.common.images.tag" ( dict "imageRoot" .imageRoot "context" .context) -}} + + {{- if $registryName -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag | quote -}} + {{- else -}} + {{- printf "%s:%s" $repositoryName $tag | quote -}} + {{- end -}} +{{- end -}} + + + +{{- /* +Return the proper image registry +{{ include "newrelic.common.images.registry" ( dict "imageRoot" .Values.path.to.the.image "defaultRegistry" "your.private.registry.tld" "context" .) }} +*/ -}} +{{- define "newrelic.common.images.registry" -}} +{{- $globalRegistry := "" -}} +{{- if .context.Values.global -}} + {{- if .context.Values.global.images -}} + {{- with .context.Values.global.images.registry -}} + {{- $globalRegistry = . -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- $localRegistry := "" -}} +{{- if .imageRoot.registry -}} + {{- $localRegistry = .imageRoot.registry -}} +{{- end -}} + +{{- $registry := $localRegistry | default $globalRegistry | default .defaultRegistry -}} +{{- if $registry -}} + {{- $registry -}} +{{- end -}} +{{- end -}} + + + +{{- /* +Return the proper image repository +{{ include "newrelic.common.images.repository" .Values.path.to.the.image }} +*/ -}} +{{- define "newrelic.common.images.repository" -}} + {{- .repository -}} +{{- end -}} + + + +{{- /* +Return the proper image tag +{{ include "newrelic.common.images.tag" ( dict "imageRoot" .Values.path.to.the.image "context" .) }} +*/ -}} +{{- define "newrelic.common.images.tag" -}} + {{- .imageRoot.tag | default .context.Chart.AppVersion | toString -}} +{{- end -}} + + + +{{- /* +Return the proper Image Pull Registry Secret Names evaluating values as templates +{{ include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" (list .Values.path.to.the.images.pullSecrets1, .Values.path.to.the.images.pullSecrets2) "context" .) }} +*/ -}} +{{- define "newrelic.common.images.renderPullSecrets" -}} + {{- $flatlist := list }} + + {{- if .context.Values.global -}} + {{- if .context.Values.global.images -}} + {{- if .context.Values.global.images.pullSecrets -}} + {{- range .context.Values.global.images.pullSecrets -}} + {{- $flatlist = append $flatlist . -}} + {{- end -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- range .pullSecrets -}} + {{- if not (empty .) -}} + {{- range . -}} + {{- $flatlist = append $flatlist . -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- if $flatlist -}} + {{- toYaml $flatlist -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_insights.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_insights.tpl new file mode 100644 index 0000000000..895c377326 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_insights.tpl @@ -0,0 +1,56 @@ +{{/* +Return the name of the secret holding the Insights Key. +*/}} +{{- define "newrelic.common.insightsKey.secretName" -}} +{{- $default := include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "insightskey" ) -}} +{{- include "newrelic.common.insightsKey._customSecretName" . | default $default -}} +{{- end -}} + +{{/* +Return the name key for the Insights Key inside the secret. +*/}} +{{- define "newrelic.common.insightsKey.secretKeyName" -}} +{{- include "newrelic.common.insightsKey._customSecretKey" . | default "insightsKey" -}} +{{- end -}} + +{{/* +Return local insightsKey if set, global otherwise. +This helper is for internal use. +*/}} +{{- define "newrelic.common.insightsKey._licenseKey" -}} +{{- if .Values.insightsKey -}} + {{- .Values.insightsKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.insightsKey -}} + {{- .Values.global.insightsKey -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name of the secret holding the Insights Key. +This helper is for internal use. +*/}} +{{- define "newrelic.common.insightsKey._customSecretName" -}} +{{- if .Values.customInsightsKeySecretName -}} + {{- .Values.customInsightsKeySecretName -}} +{{- else if .Values.global -}} + {{- if .Values.global.customInsightsKeySecretName -}} + {{- .Values.global.customInsightsKeySecretName -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name key for the Insights Key inside the secret. +This helper is for internal use. +*/}} +{{- define "newrelic.common.insightsKey._customSecretKey" -}} +{{- if .Values.customInsightsKeySecretKey -}} + {{- .Values.customInsightsKeySecretKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.customInsightsKeySecretKey }} + {{- .Values.global.customInsightsKeySecretKey -}} + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_insights_secret.yaml.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_insights_secret.yaml.tpl new file mode 100644 index 0000000000..556caa6ca6 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_insights_secret.yaml.tpl @@ -0,0 +1,21 @@ +{{/* +Renders the insights key secret if user has not specified a custom secret. +*/}} +{{- define "newrelic.common.insightsKey.secret" }} +{{- if not (include "newrelic.common.insightsKey._customSecretName" .) }} +{{- /* Fail if licenseKey is empty and required: */ -}} +{{- if not (include "newrelic.common.insightsKey._licenseKey" .) }} + {{- fail "You must specify a insightsKey or a customInsightsSecretName containing it" }} +{{- end }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "newrelic.common.insightsKey.secretName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +data: + {{ include "newrelic.common.insightsKey.secretKeyName" . }}: {{ include "newrelic.common.insightsKey._licenseKey" . | b64enc }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_labels.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_labels.tpl new file mode 100644 index 0000000000..b025948285 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_labels.tpl @@ -0,0 +1,54 @@ +{{/* +This will render the labels that should be used in all the manifests used by the helm chart. +*/}} +{{- define "newrelic.common.labels" -}} +{{- $global := index .Values "global" | default dict -}} + +{{- $chart := dict "helm.sh/chart" (include "newrelic.common.naming.chart" . ) -}} +{{- $managedBy := dict "app.kubernetes.io/managed-by" .Release.Service -}} +{{- $selectorLabels := fromYaml (include "newrelic.common.labels.selectorLabels" . ) -}} + +{{- $labels := mustMergeOverwrite $chart $managedBy $selectorLabels -}} +{{- if .Chart.AppVersion -}} +{{- $labels = mustMergeOverwrite $labels (dict "app.kubernetes.io/version" .Chart.AppVersion) -}} +{{- end -}} + +{{- $globalUserLabels := $global.labels | default dict -}} +{{- $localUserLabels := .Values.labels | default dict -}} + +{{- $labels = mustMergeOverwrite $labels $globalUserLabels $localUserLabels -}} + +{{- toYaml $labels -}} +{{- end -}} + + + +{{/* +This will render the labels that should be used in deployments/daemonsets template pods as a selector. +*/}} +{{- define "newrelic.common.labels.selectorLabels" -}} +{{- $name := dict "app.kubernetes.io/name" ( include "newrelic.common.naming.name" . ) -}} +{{- $instance := dict "app.kubernetes.io/instance" .Release.Name -}} + +{{- $selectorLabels := mustMergeOverwrite $name $instance -}} + +{{- toYaml $selectorLabels -}} +{{- end }} + + + +{{/* +Pod labels +*/}} +{{- define "newrelic.common.labels.podLabels" -}} +{{- $selectorLabels := fromYaml (include "newrelic.common.labels.selectorLabels" . ) -}} + +{{- $global := index .Values "global" | default dict -}} +{{- $globalPodLabels := $global.podLabels | default dict }} + +{{- $localPodLabels := .Values.podLabels | default dict }} + +{{- $podLabels := mustMergeOverwrite $selectorLabels $globalPodLabels $localPodLabels -}} + +{{- toYaml $podLabels -}} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_license.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_license.tpl new file mode 100644 index 0000000000..647b4ff439 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_license.tpl @@ -0,0 +1,56 @@ +{{/* +Return the name of the secret holding the License Key. +*/}} +{{- define "newrelic.common.license.secretName" -}} +{{- $default := include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "license" ) -}} +{{- include "newrelic.common.license._customSecretName" . | default $default -}} +{{- end -}} + +{{/* +Return the name key for the License Key inside the secret. +*/}} +{{- define "newrelic.common.license.secretKeyName" -}} +{{- include "newrelic.common.license._customSecretKey" . | default "licenseKey" -}} +{{- end -}} + +{{/* +Return local licenseKey if set, global otherwise. +This helper is for internal use. +*/}} +{{- define "newrelic.common.license._licenseKey" -}} +{{- if .Values.licenseKey -}} + {{- .Values.licenseKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.licenseKey -}} + {{- .Values.global.licenseKey -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name of the secret holding the License Key. +This helper is for internal use. +*/}} +{{- define "newrelic.common.license._customSecretName" -}} +{{- if .Values.customSecretName -}} + {{- .Values.customSecretName -}} +{{- else if .Values.global -}} + {{- if .Values.global.customSecretName -}} + {{- .Values.global.customSecretName -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name key for the License Key inside the secret. +This helper is for internal use. +*/}} +{{- define "newrelic.common.license._customSecretKey" -}} +{{- if .Values.customSecretLicenseKey -}} + {{- .Values.customSecretLicenseKey -}} +{{- else if .Values.global -}} + {{- if .Values.global.customSecretLicenseKey }} + {{- .Values.global.customSecretLicenseKey -}} + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_license_secret.yaml.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_license_secret.yaml.tpl new file mode 100644 index 0000000000..610a0a3370 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_license_secret.yaml.tpl @@ -0,0 +1,21 @@ +{{/* +Renders the license key secret if user has not specified a custom secret. +*/}} +{{- define "newrelic.common.license.secret" }} +{{- if not (include "newrelic.common.license._customSecretName" .) }} +{{- /* Fail if licenseKey is empty and required: */ -}} +{{- if not (include "newrelic.common.license._licenseKey" .) }} + {{- fail "You must specify a licenseKey or a customSecretName containing it" }} +{{- end }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "newrelic.common.license.secretName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +data: + {{ include "newrelic.common.license.secretKeyName" . }}: {{ include "newrelic.common.license._licenseKey" . | b64enc }} +{{- end }} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_low-data-mode.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_low-data-mode.tpl new file mode 100644 index 0000000000..3dd55ef2ff --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_low-data-mode.tpl @@ -0,0 +1,26 @@ +{{- /* +Abstraction of the lowDataMode toggle. +This helper allows to override the global `.global.lowDataMode` with the value of `.lowDataMode`. +Returns "true" if `lowDataMode` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.lowDataMode" -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if (get .Values "lowDataMode" | kindIs "bool") -}} + {{- if .Values.lowDataMode -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.lowDataMode" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.lowDataMode -}} + {{- end -}} +{{- else -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "lowDataMode" | kindIs "bool" -}} + {{- if $global.lowDataMode -}} + {{- $global.lowDataMode -}} + {{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_naming.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_naming.tpl new file mode 100644 index 0000000000..19fa92648c --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_naming.tpl @@ -0,0 +1,73 @@ +{{/* +This is an function to be called directly with a string just to truncate strings to +63 chars because some Kubernetes name fields are limited to that. +*/}} +{{- define "newrelic.common.naming.truncateToDNS" -}} +{{- . | trunc 63 | trimSuffix "-" }} +{{- end }} + + + +{{- /* +Given a name and a suffix returns a 'DNS Valid' which always include the suffix, truncating the name if needed. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If suffix is too long it gets truncated but it always takes precedence over name, so a 63 chars suffix would suppress the name. +Usage: +{{ include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" "" "suffix" "my-suffix" ) }} +*/ -}} +{{- define "newrelic.common.naming.truncateToDNSWithSuffix" -}} +{{- $suffix := (include "newrelic.common.naming.truncateToDNS" .suffix) -}} +{{- $maxLen := (max (sub 63 (add1 (len $suffix))) 0) -}} {{- /* We prepend "-" to the suffix so an additional character is needed */ -}} + +{{- $newName := .name | trunc ($maxLen | int) | trimSuffix "-" -}} +{{- if $newName -}} +{{- printf "%s-%s" $newName $suffix -}} +{{- else -}} +{{ $suffix }} +{{- end -}} + +{{- end -}} + + + +{{/* +Expand the name of the chart. +Uses the Chart name by default if nameOverride is not set. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "newrelic.common.naming.name" -}} +{{- $name := .Values.nameOverride | default .Chart.Name -}} +{{- include "newrelic.common.naming.truncateToDNS" $name -}} +{{- end }} + + + +{{/* +Create a default fully qualified app name. +By default the full name will be "" just in if it has the chart name included in that, if not +it will be concatenated like "-". This could change if fullnameOverride or +nameOverride are set. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "newrelic.common.naming.fullname" -}} +{{- $name := include "newrelic.common.naming.name" . -}} + +{{- if .Values.fullnameOverride -}} + {{- $name = .Values.fullnameOverride -}} +{{- else if not (contains $name .Release.Name) -}} + {{- $name = printf "%s-%s" .Release.Name $name -}} +{{- end -}} + +{{- include "newrelic.common.naming.truncateToDNS" $name -}} + +{{- end -}} + + + +{{/* +Create chart name and version as used by the chart label. +This function should not be used for naming objects. Use "common.naming.{name,fullname}" instead. +*/}} +{{- define "newrelic.common.naming.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_nodeselector.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_nodeselector.tpl new file mode 100644 index 0000000000..d488873412 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_nodeselector.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod nodeSelector */ -}} +{{- define "newrelic.common.nodeSelector" -}} + {{- if .Values.nodeSelector -}} + {{- toYaml .Values.nodeSelector -}} + {{- else if .Values.global -}} + {{- if .Values.global.nodeSelector -}} + {{- toYaml .Values.global.nodeSelector -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_priority-class-name.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_priority-class-name.tpl new file mode 100644 index 0000000000..50182b7343 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_priority-class-name.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the pod priorityClassName */ -}} +{{- define "newrelic.common.priorityClassName" -}} + {{- if .Values.priorityClassName -}} + {{- .Values.priorityClassName -}} + {{- else if .Values.global -}} + {{- if .Values.global.priorityClassName -}} + {{- .Values.global.priorityClassName -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_privileged.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_privileged.tpl new file mode 100644 index 0000000000..f3ae814dd9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_privileged.tpl @@ -0,0 +1,28 @@ +{{- /* +This is a helper that returns whether the chart should assume the user is fine deploying privileged pods. +*/ -}} +{{- define "newrelic.common.privileged" -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists. */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if get .Values "privileged" | kindIs "bool" -}} + {{- if .Values.privileged -}} + {{- .Values.privileged -}} + {{- end -}} +{{- else if get $global "privileged" | kindIs "bool" -}} + {{- if $global.privileged -}} + {{- $global.privileged -}} + {{- end -}} +{{- end -}} +{{- end -}} + + + +{{- /* Return directly "true" or "false" based in the exist of "newrelic.common.privileged" */ -}} +{{- define "newrelic.common.privileged.value" -}} +{{- if include "newrelic.common.privileged" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_proxy.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_proxy.tpl new file mode 100644 index 0000000000..60f34c7ec1 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_proxy.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the proxy */ -}} +{{- define "newrelic.common.proxy" -}} + {{- if .Values.proxy -}} + {{- .Values.proxy -}} + {{- else if .Values.global -}} + {{- if .Values.global.proxy -}} + {{- .Values.global.proxy -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_security-context.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_security-context.tpl new file mode 100644 index 0000000000..9edfcabfd0 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_security-context.tpl @@ -0,0 +1,23 @@ +{{- /* Defines the container securityContext context */ -}} +{{- define "newrelic.common.securityContext.container" -}} +{{- $global := index .Values "global" | default dict -}} + +{{- if .Values.containerSecurityContext -}} + {{- toYaml .Values.containerSecurityContext -}} +{{- else if $global.containerSecurityContext -}} + {{- toYaml $global.containerSecurityContext -}} +{{- end -}} +{{- end -}} + + + +{{- /* Defines the pod securityContext context */ -}} +{{- define "newrelic.common.securityContext.pod" -}} +{{- $global := index .Values "global" | default dict -}} + +{{- if .Values.podSecurityContext -}} + {{- toYaml .Values.podSecurityContext -}} +{{- else if $global.podSecurityContext -}} + {{- toYaml $global.podSecurityContext -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_serviceaccount.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_serviceaccount.tpl new file mode 100644 index 0000000000..2d352f6ea9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_serviceaccount.tpl @@ -0,0 +1,90 @@ +{{- /* Defines if the service account has to be created or not */ -}} +{{- define "newrelic.common.serviceAccount.create" -}} +{{- $valueFound := false -}} + +{{- /* Look for a global creation of a service account */ -}} +{{- if get .Values "serviceAccount" | kindIs "map" -}} + {{- if (get .Values.serviceAccount "create" | kindIs "bool") -}} + {{- $valueFound = true -}} + {{- if .Values.serviceAccount.create -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.serviceAccount.name" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.serviceAccount.create -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- /* Look for a local creation of a service account */ -}} +{{- if not $valueFound -}} + {{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} + {{- $global := index .Values "global" | default dict -}} + {{- if get $global "serviceAccount" | kindIs "map" -}} + {{- if get $global.serviceAccount "create" | kindIs "bool" -}} + {{- $valueFound = true -}} + {{- if $global.serviceAccount.create -}} + {{- $global.serviceAccount.create -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- /* In case no serviceAccount value has been found, default to "true" */ -}} +{{- if not $valueFound -}} +true +{{- end -}} +{{- end -}} + + + +{{- /* Defines the name of the service account */ -}} +{{- define "newrelic.common.serviceAccount.name" -}} +{{- $localServiceAccount := "" -}} +{{- if get .Values "serviceAccount" | kindIs "map" -}} + {{- if (get .Values.serviceAccount "name" | kindIs "string") -}} + {{- $localServiceAccount = .Values.serviceAccount.name -}} + {{- end -}} +{{- end -}} + +{{- $globalServiceAccount := "" -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "serviceAccount" | kindIs "map" -}} + {{- if get $global.serviceAccount "name" | kindIs "string" -}} + {{- $globalServiceAccount = $global.serviceAccount.name -}} + {{- end -}} +{{- end -}} + +{{- if (include "newrelic.common.serviceAccount.create" .) -}} + {{- $localServiceAccount | default $globalServiceAccount | default (include "newrelic.common.naming.fullname" .) -}} +{{- else -}} + {{- $localServiceAccount | default $globalServiceAccount | default "default" -}} +{{- end -}} +{{- end -}} + + + +{{- /* Merge the global and local annotations for the service account */ -}} +{{- define "newrelic.common.serviceAccount.annotations" -}} +{{- $localServiceAccount := dict -}} +{{- if get .Values "serviceAccount" | kindIs "map" -}} + {{- if get .Values.serviceAccount "annotations" -}} + {{- $localServiceAccount = .Values.serviceAccount.annotations -}} + {{- end -}} +{{- end -}} + +{{- $globalServiceAccount := dict -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "serviceAccount" | kindIs "map" -}} + {{- if get $global.serviceAccount "annotations" -}} + {{- $globalServiceAccount = $global.serviceAccount.annotations -}} + {{- end -}} +{{- end -}} + +{{- $merged := mustMergeOverwrite $globalServiceAccount $localServiceAccount -}} + +{{- if $merged -}} + {{- toYaml $merged -}} +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_staging.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_staging.tpl new file mode 100644 index 0000000000..bd9ad09bb9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_staging.tpl @@ -0,0 +1,39 @@ +{{- /* +Abstraction of the nrStaging toggle. +This helper allows to override the global `.global.nrStaging` with the value of `.nrStaging`. +Returns "true" if `nrStaging` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.nrStaging" -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if (get .Values "nrStaging" | kindIs "bool") -}} + {{- if .Values.nrStaging -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.nrStaging" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.nrStaging -}} + {{- end -}} +{{- else -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "nrStaging" | kindIs "bool" -}} + {{- if $global.nrStaging -}} + {{- $global.nrStaging -}} + {{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} + + + +{{- /* +Returns "true" of "false" directly instead of empty string (Helm falsiness) based on the exit of "newrelic.common.nrStaging" +*/ -}} +{{- define "newrelic.common.nrStaging.value" -}} +{{- if include "newrelic.common.nrStaging" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_tolerations.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_tolerations.tpl new file mode 100644 index 0000000000..e016b38e27 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_tolerations.tpl @@ -0,0 +1,10 @@ +{{- /* Defines the Pod tolerations */ -}} +{{- define "newrelic.common.tolerations" -}} + {{- if .Values.tolerations -}} + {{- toYaml .Values.tolerations -}} + {{- else if .Values.global -}} + {{- if .Values.global.tolerations -}} + {{- toYaml .Values.global.tolerations -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_verbose-log.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_verbose-log.tpl new file mode 100644 index 0000000000..2286d46815 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/templates/_verbose-log.tpl @@ -0,0 +1,54 @@ +{{- /* +Abstraction of the verbose toggle. +This helper allows to override the global `.global.verboseLog` with the value of `.verboseLog`. +Returns "true" if `verbose` is enabled, otherwise "" (empty string) +*/ -}} +{{- define "newrelic.common.verboseLog" -}} +{{- /* `get` will return "" (empty string) if value is not found, and the value otherwise, so we can type-assert with kindIs */ -}} +{{- if (get .Values "verboseLog" | kindIs "bool") -}} + {{- if .Values.verboseLog -}} + {{- /* + We want only to return when this is true, returning `false` here will template "false" (string) when doing + an `(include "newrelic.common.verboseLog" .)`, which is not an "empty string" so it is `true` if it is used + as an evaluation somewhere else. + */ -}} + {{- .Values.verboseLog -}} + {{- end -}} +{{- else -}} +{{- /* This allows us to use `$global` as an empty dict directly in case `Values.global` does not exists */ -}} +{{- $global := index .Values "global" | default dict -}} +{{- if get $global "verboseLog" | kindIs "bool" -}} + {{- if $global.verboseLog -}} + {{- $global.verboseLog -}} + {{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} + + + +{{- /* +Abstraction of the verbose toggle. +This helper abstracts the function "newrelic.common.verboseLog" to return true or false directly. +*/ -}} +{{- define "newrelic.common.verboseLog.valueAsBoolean" -}} +{{- if include "newrelic.common.verboseLog" . -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} + + + +{{- /* +Abstraction of the verbose toggle. +This helper abstracts the function "newrelic.common.verboseLog" to return 1 or 0 directly. +*/ -}} +{{- define "newrelic.common.verboseLog.valueAsInt" -}} +{{- if include "newrelic.common.verboseLog" . -}} +1 +{{- else -}} +0 +{{- end -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/values.yaml new file mode 100644 index 0000000000..75e2d112ad --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/charts/common-library/values.yaml @@ -0,0 +1 @@ +# values are not needed for the library chart, however this file is still needed for helm lint to work. diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/ci/test-lowdatamode-values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/ci/test-lowdatamode-values.yaml new file mode 100644 index 0000000000..57b307a2d0 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/ci/test-lowdatamode-values.yaml @@ -0,0 +1,9 @@ +global: + licenseKey: 1234567890abcdef1234567890abcdef12345678 + cluster: test-cluster + +lowDataMode: true + +image: + repository: e2e/nri-prometheus + tag: "test" # Defaults to chart's appVersion diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/ci/test-override-global-lowdatamode.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/ci/test-override-global-lowdatamode.yaml new file mode 100644 index 0000000000..7ff1a730fa --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/ci/test-override-global-lowdatamode.yaml @@ -0,0 +1,10 @@ +global: + licenseKey: 1234567890abcdef1234567890abcdef12345678 + cluster: test-cluster + lowDataMode: true + +lowDataMode: false + +image: + repository: e2e/nri-prometheus + tag: "test" # Defaults to chart's appVersion diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/ci/test-values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/ci/test-values.yaml new file mode 100644 index 0000000000..fcd07b2d3e --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/ci/test-values.yaml @@ -0,0 +1,104 @@ +global: + licenseKey: 1234567890abcdef1234567890abcdef12345678 + cluster: test-cluster + +lowDataMode: true + +nameOverride: my-custom-name + +image: + registry: + repository: e2e/nri-prometheus + tag: "test" + imagePullPolicy: IfNotPresent + +resources: + limits: + cpu: 200m + memory: 512Mi + requests: + cpu: 100m + memory: 256Mi + +rbac: + create: true + +serviceAccount: + # Specifies whether a ServiceAccount should be created + create: true + # The name of the ServiceAccount to use. + # If not set and create is true, a name is generated using the name template + name: "" + # Specify any annotations to add to the ServiceAccount + annotations: + foo: bar + +# If you wish to provide your own config.yaml file include it under config: +# the sample config file is included here as an example. +config: + scrape_duration: "60s" + scrape_timeout: "15s" + + scrape_services: false + scrape_endpoints: true + + audit: false + + insecure_skip_verify: false + + scrape_enabled_label: "prometheus.io/scrape" + + require_scrape_enabled_label_for_nodes: true + + transformations: + - description: "Custom transformation Example" + rename_attributes: + - metric_prefix: "foo_" + attributes: + old_label: "newLabel" + ignore_metrics: + - prefixes: + - bar_ + copy_attributes: + - from_metric: "foo_info" + to_metrics: "foo_" + match_by: + - namespace + +podAnnotations: + custom-pod-annotation: test + +podSecurityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 2000 + +containerSecurityContext: + runAsUser: 2000 + +tolerations: + - key: "key1" + operator: "Exists" + effect: "NoSchedule" + +nodeSelector: + kubernetes.io/os: linux + +affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/os + operator: In + values: + - linux + +nrStaging: false + +fedramp: + enabled: true + +proxy: + +verboseLog: true diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/static/lowdatamodedefaults.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/static/lowdatamodedefaults.yaml new file mode 100644 index 0000000000..f749e28da4 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/static/lowdatamodedefaults.yaml @@ -0,0 +1,10 @@ +transformations: + - description: "Low data mode defaults" + ignore_metrics: + # Ignore the following metrics. + # These metrics are already collected by the New Relic Kubernetes Integration. + - prefixes: + - kube_ + - container_ + - machine_ + - cadvisor_ diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/templates/_helpers.tpl b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/templates/_helpers.tpl new file mode 100644 index 0000000000..23c072bd72 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/templates/_helpers.tpl @@ -0,0 +1,15 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* +Returns mergeTransformations +Helm can't merge maps of different types. Need to manually create a `transformations` section. +*/}} +{{- define "nri-prometheus.mergeTransformations" -}} + {{/* Remove current `transformations` from config. */}} + {{- omit .Values.config "transformations" | toYaml | nindent 4 -}} + {{/* Create new `transformations` yaml section with merged configs from .Values.config.transformations and lowDataMode. */}} + transformations: + {{- .Values.config.transformations | toYaml | nindent 4 -}} + {{ $lowDataDefault := .Files.Get "static/lowdatamodedefaults.yaml" | fromYaml }} + {{- $lowDataDefault.transformations | toYaml | nindent 4 -}} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/templates/clusterrole.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/templates/clusterrole.yaml new file mode 100644 index 0000000000..ac4734d31e --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/templates/clusterrole.yaml @@ -0,0 +1,23 @@ +{{- if .Values.rbac.create }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "newrelic.common.naming.fullname" . }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +rules: +- apiGroups: [""] + resources: + - "nodes" + - "nodes/metrics" + - "nodes/stats" + - "nodes/proxy" + - "pods" + - "services" + - "endpoints" + verbs: ["get", "list", "watch"] +- nonResourceURLs: + - /metrics + verbs: + - get +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/templates/clusterrolebinding.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/templates/clusterrolebinding.yaml new file mode 100644 index 0000000000..44244653ff --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/templates/clusterrolebinding.yaml @@ -0,0 +1,16 @@ +{{- if .Values.rbac.create }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "newrelic.common.naming.fullname" . }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "newrelic.common.naming.fullname" . }} +subjects: +- kind: ServiceAccount + name: {{ include "newrelic.common.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/templates/configmap.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/templates/configmap.yaml new file mode 100644 index 0000000000..5daeed64ad --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/templates/configmap.yaml @@ -0,0 +1,21 @@ +kind: ConfigMap +metadata: + name: {{ include "newrelic.common.naming.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +apiVersion: v1 +data: + config.yaml: | + cluster_name: {{ include "newrelic.common.cluster" . }} +{{- if .Values.config -}} + {{- if and (.Values.config.transformations) (include "newrelic.common.lowDataMode" .) -}} + {{- include "nri-prometheus.mergeTransformations" . -}} + {{- else if (include "newrelic.common.lowDataMode" .) -}} + {{ $lowDataDefault := .Files.Get "static/lowdatamodedefaults.yaml" | fromYaml }} + {{- mergeOverwrite (deepCopy .Values.config) $lowDataDefault | toYaml | nindent 4 -}} + {{- else }} + {{- .Values.config | toYaml | nindent 4 -}} + {{- end -}} +{{- end -}} + diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/templates/deployment.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/templates/deployment.yaml new file mode 100644 index 0000000000..8529b71f40 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/templates/deployment.yaml @@ -0,0 +1,98 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "newrelic.common.naming.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +spec: + replicas: 1 + selector: + matchLabels: + {{- /* We cannot use the common library here because of a legacy issue */}} + {{- /* `selector` is inmutable and the previous chart did not have all the idiomatic labels */}} + app.kubernetes.io/name: {{ include "newrelic.common.naming.name" . }} + template: + metadata: + annotations: + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "newrelic.common.labels.podLabels" . | nindent 8 }} + spec: + serviceAccountName: {{ include "newrelic.common.serviceAccount.name" . }} + {{- with include "newrelic.common.securityContext.pod" . }} + securityContext: + {{- . | nindent 8 }} + {{- end }} + {{- with include "newrelic.common.images.renderPullSecrets" ( dict "pullSecrets" (list .Values.image.pullSecrets) "context" .) }} + imagePullSecrets: + {{- . | nindent 8 }} + {{- end }} + containers: + - name: nri-prometheus + {{- with include "newrelic.common.securityContext.container" . }} + securityContext: + {{- . | nindent 10 }} + {{- end }} + image: {{ include "newrelic.common.images.image" ( dict "imageRoot" .Values.image "context" .) }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + args: + - "--configfile=/etc/nri-prometheus/config.yaml" + ports: + - containerPort: 8080 + volumeMounts: + - name: config-volume + mountPath: /etc/nri-prometheus/ + env: + - name: "LICENSE_KEY" + valueFrom: + secretKeyRef: + name: {{ include "newrelic.common.license.secretName" . }} + key: {{ include "newrelic.common.license.secretKeyName" . }} + {{- if (include "newrelic.common.nrStaging" .) }} + - name: "METRIC_API_URL" + value: "https://staging-metric-api.newrelic.com/metric/v1/infra" + {{- else if (include "newrelic.common.fedramp.enabled" .) }} + - name: "METRIC_API_URL" + value: "https://gov-metric-api.newrelic.com/metric/v1" + {{- end }} + {{- with include "newrelic.common.proxy" . }} + - name: EMITTER_PROXY + value: {{ . | quote }} + {{- end }} + {{- with include "newrelic.common.verboseLog" . }} + - name: "VERBOSE" + value: {{ . | quote }} + {{- end }} + - name: "BEARER_TOKEN_FILE" + value: "/var/run/secrets/kubernetes.io/serviceaccount/token" + - name: "CA_FILE" + value: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" + resources: + {{- toYaml .Values.resources | nindent 10 }} + volumes: + - name: config-volume + configMap: + name: {{ include "newrelic.common.naming.fullname" . }} + {{- with include "newrelic.common.priorityClassName" . }} + priorityClassName: {{ . }} + {{- end }} + {{- with include "newrelic.common.dnsConfig" . }} + dnsConfig: + {{- . | nindent 8 }} + {{- end }} + {{- with include "newrelic.common.nodeSelector" . }} + nodeSelector: + {{- . | nindent 8 }} + {{- end }} + {{- with include "newrelic.common.affinity" . }} + affinity: + {{- . | nindent 8 }} + {{- end }} + {{- with include "newrelic.common.tolerations" . }} + tolerations: + {{- . | nindent 8 }} + {{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/templates/secret.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/templates/secret.yaml new file mode 100644 index 0000000000..f558ee86c9 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/templates/secret.yaml @@ -0,0 +1,2 @@ +{{- /* Common library will take care of creating the secret or not. */}} +{{- include "newrelic.common.license.secret" . }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/templates/serviceaccount.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/templates/serviceaccount.yaml new file mode 100644 index 0000000000..df451ec905 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if (include "newrelic.common.serviceAccount.create" .) }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "newrelic.common.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} + {{- with (include "newrelic.common.serviceAccount.annotations" .) }} + annotations: + {{- . | nindent 4 }} + {{- end }} +{{- end -}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/tests/configmap_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/tests/configmap_test.yaml new file mode 100644 index 0000000000..ae7d921fe6 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/tests/configmap_test.yaml @@ -0,0 +1,86 @@ +suite: test nri-prometheus configmap +templates: + - templates/configmap.yaml + - templates/deployment.yaml +tests: + - it: creates the config map with default config in values.yaml and cluster_name. + set: + licenseKey: fakeLicense + cluster: my-cluster-name + asserts: + - equal: + path: data["config.yaml"] + value: |- + cluster_name: my-cluster-name + audit: false + insecure_skip_verify: false + require_scrape_enabled_label_for_nodes: true + scrape_enabled_label: prometheus.io/scrape + scrape_endpoints: false + scrape_services: true + transformations: [] + template: templates/configmap.yaml + + - it: creates the config map with lowDataMode. + set: + licenseKey: fakeLicense + cluster: my-cluster-name + lowDataMode: true + asserts: + - equal: + path: data["config.yaml"] + value: |- + cluster_name: my-cluster-name + audit: false + insecure_skip_verify: false + require_scrape_enabled_label_for_nodes: true + scrape_enabled_label: prometheus.io/scrape + scrape_endpoints: false + scrape_services: true + transformations: + - description: Low data mode defaults + ignore_metrics: + - prefixes: + - kube_ + - container_ + - machine_ + - cadvisor_ + template: templates/configmap.yaml + + - it: merges existing transformation with lowDataMode. + set: + licenseKey: fakeLicense + cluster: my-cluster-name + lowDataMode: true + config: + transformations: + - description: Custom transformation Example + rename_attributes: + - metric_prefix: test_ + attributes: + container_name: containerName + asserts: + - equal: + path: data["config.yaml"] + value: |- + cluster_name: my-cluster-name + audit: false + insecure_skip_verify: false + require_scrape_enabled_label_for_nodes: true + scrape_enabled_label: prometheus.io/scrape + scrape_endpoints: false + scrape_services: true + transformations: + - description: Custom transformation Example + rename_attributes: + - attributes: + container_name: containerName + metric_prefix: test_ + - description: Low data mode defaults + ignore_metrics: + - prefixes: + - kube_ + - container_ + - machine_ + - cadvisor_ + template: templates/configmap.yaml diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/tests/deployment_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/tests/deployment_test.yaml new file mode 100644 index 0000000000..cb6f903405 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/tests/deployment_test.yaml @@ -0,0 +1,82 @@ +suite: test deployment +templates: + - templates/deployment.yaml + - templates/configmap.yaml + +release: + name: release + +tests: + - it: adds defaults. + set: + licenseKey: fakeLicense + cluster: test + asserts: + - equal: + path: spec.template.metadata.labels["app.kubernetes.io/instance"] + value: release + template: templates/deployment.yaml + - equal: + path: spec.template.metadata.labels["app.kubernetes.io/name"] + value: nri-prometheus + template: templates/deployment.yaml + - equal: + path: spec.selector.matchLabels + value: + app.kubernetes.io/name: nri-prometheus + template: templates/deployment.yaml + - isNotEmpty: + path: spec.template.metadata.annotations["checksum/config"] + template: templates/deployment.yaml + + - it: adds METRIC_API_URL when nrStaging is true. + set: + licenseKey: fakeLicense + cluster: test + nrStaging: true + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: "METRIC_API_URL" + value: "https://staging-metric-api.newrelic.com/metric/v1/infra" + template: templates/deployment.yaml + + - it: adds FedRamp endpoint when FedRamp is enabled. + set: + licenseKey: fakeLicense + cluster: test + fedramp: + enabled: true + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: "METRIC_API_URL" + value: "https://gov-metric-api.newrelic.com/metric/v1" + template: templates/deployment.yaml + + - it: adds proxy when enabled. + set: + licenseKey: fakeLicense + cluster: test + proxy: "https://my-proxy:9999" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: "EMITTER_PROXY" + value: "https://my-proxy:9999" + template: templates/deployment.yaml + + - it: set priorityClassName. + set: + licenseKey: fakeLicense + cluster: test + priorityClassName: foo + asserts: + - equal: + path: spec.template.spec.priorityClassName + value: foo + template: templates/deployment.yaml + diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/tests/labels_test.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/tests/labels_test.yaml new file mode 100644 index 0000000000..2b6cb53bb1 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/tests/labels_test.yaml @@ -0,0 +1,32 @@ +suite: test object names +templates: + - templates/clusterrole.yaml + - templates/clusterrolebinding.yaml + - templates/configmap.yaml + - templates/deployment.yaml + - templates/secret.yaml + - templates/serviceaccount.yaml + +release: + name: release + revision: + +tests: + - it: adds default labels. + set: + licenseKey: fakeLicense + cluster: test + asserts: + - equal: + path: metadata.labels["app.kubernetes.io/instance"] + value: release + - equal: + path: metadata.labels["app.kubernetes.io/managed-by"] + value: Helm + - equal: + path: metadata.labels["app.kubernetes.io/name"] + value: nri-prometheus + - isNotEmpty: + path: metadata.labels["app.kubernetes.io/version"] + - isNotEmpty: + path: metadata.labels["helm.sh/chart"] diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/values.yaml new file mode 100644 index 0000000000..4c562cc660 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/nri-prometheus/values.yaml @@ -0,0 +1,251 @@ +# -- Override the name of the chart +nameOverride: "" +# -- Override the full name of the release +fullnameOverride: "" + +# -- Name of the Kubernetes cluster monitored. Can be configured also with `global.cluster` +cluster: "" +# -- This set this license key to use. Can be configured also with `global.licenseKey` +licenseKey: "" +# -- In case you don't want to have the license key in you values, this allows you to point to a user created secret to get the key from there. Can be configured also with `global.customSecretName` +customSecretName: "" +# -- In case you don't want to have the license key in you values, this allows you to point to which secret key is the license key located. Can be configured also with `global.customSecretLicenseKey` +customSecretLicenseKey: "" + +# -- Image for the New Relic Kubernetes integration +# @default -- See `values.yaml` +image: + registry: + repository: newrelic/nri-prometheus + tag: "" # Defaults to chart's appVersion + imagePullPolicy: IfNotPresent + # -- The secrets that are needed to pull images from a custom registry. + pullSecrets: [] + # - name: regsecret + +resources: {} + # limits: + # cpu: 200m + # memory: 512Mi + # requests: + # cpu: 100m + # memory: 256Mi + +rbac: + # -- Specifies whether RBAC resources should be created + create: true + +serviceAccount: + # -- Add these annotations to the service account we create. Can be configured also with `global.serviceAccount.annotations` + annotations: {} + # -- Configures if the service account should be created or not. Can be configured also with `global.serviceAccount.create` + create: true + # -- Change the name of the service account. This is honored if you disable on this cahrt the creation of the service account so you can use your own. Can be configured also with `global.serviceAccount.name` + name: + +# -- Annotations to be added to all pods created by the integration. +podAnnotations: {} +# -- Additional labels for chart pods. Can be configured also with `global.podLabels` +podLabels: {} +# -- Additional labels for chart objects. Can be configured also with `global.labels` +labels: {} + +# -- Sets pod's priorityClassName. Can be configured also with `global.priorityClassName` +priorityClassName: "" +# -- (bool) Sets pod's hostNetwork. Can be configured also with `global.hostNetwork` +# @default -- `false` +hostNetwork: +# -- Sets pod's dnsConfig. Can be configured also with `global.dnsConfig` +dnsConfig: {} + +# -- Sets security context (at pod level). Can be configured also with `global.podSecurityContext` +podSecurityContext: {} +# -- Sets security context (at container level). Can be configured also with `global.containerSecurityContext` +containerSecurityContext: {} + +# -- Sets pod/node affinities. Can be configured also with `global.affinity` +affinity: {} +# -- Sets pod's node selector. Can be configured also with `global.nodeSelector` +nodeSelector: {} +# -- Sets pod's tolerations to node taints. Can be configured also with `global.tolerations` +tolerations: [] + + +# -- Provides your own `config.yaml` for this integration. +# Ref: https://docs.newrelic.com/docs/infrastructure/prometheus-integrations/install-configure-openmetrics/configure-prometheus-openmetrics-integrations/#example-configuration-file +# @default -- See `values.yaml` +config: + # How often the integration should run. + # Default: "30s" + # scrape_duration: "30s" + + # The HTTP client timeout when fetching data from targets. + # Default: "5s" + # scrape_timeout: "5s" + + # scrape_services Allows to enable scraping the service and not the endpoints behind. + # When endpoints are scraped this is no longer needed + scrape_services: true + + # scrape_endpoints Allows to enable scraping directly endpoints instead of services as prometheus service natively does. + # Please notice that depending on the number of endpoints behind a service the load can increase considerably + scrape_endpoints: false + + # How old must the entries used for calculating the counters delta be + # before the telemetry emitter expires them. + # Default: "5m" + # telemetry_emitter_delta_expiration_age: "5m" + + # How often must the telemetry emitter check for expired delta entries. + # Default: "5m" + # telemetry_emitter_delta_expiration_check_interval: "5m" + + # Whether the integration should run in audit mode or not. Defaults to false. + # Audit mode logs the uncompressed data sent to New Relic. Use this to log all data sent. + # It does not include verbose mode. This can lead to a high log volume, use with care. + # Default: false + audit: false + + # Whether the integration should skip TLS verification or not. + # Default: false + insecure_skip_verify: false + + # The label used to identify scrapeable targets. + # Targets can be identified using a label or annotation. + # Default: "prometheus.io/scrape" + scrape_enabled_label: "prometheus.io/scrape" + + # Whether k8s nodes need to be labelled to be scraped or not. + # Default: true + require_scrape_enabled_label_for_nodes: true + + # Number of worker threads used for scraping targets. + # For large clusters with many (>400) targets, slowly increase until scrape + # time falls between the desired `scrape_duration`. + # Increasing this value too much will result in huge memory consumption if too + # many metrics are being scraped. + # Default: 4 + # worker_threads: 4 + + # Maximum number of metrics to keep in memory until a report is triggered. + # Changing this value is not recommended unless instructed by the New Relic support team. + # max_stored_metrics: 10000 + + # Minimum amount of time to wait between reports. Cannot be lowered than the default, 200ms. + # Changing this value is not recommended unless instructed by the New Relic support team. + # min_emitter_harvest_period: 200ms + + # targets: + # - description: Secure etcd example + # urls: ["https://192.168.3.1:2379", "https://192.168.3.2:2379", "https://192.168.3.3:2379"] + # If true the Kubernetes Service Account token will be included as a Bearer token in the HTTP request. + # use_bearer: false + # tls_config: + # ca_file_path: "/etc/etcd/etcd-client-ca.crt" + # cert_file_path: "/etc/etcd/etcd-client.crt" + # key_file_path: "/etc/etcd/etcd-client.key" + + # Certificate to add to the root CA that the emitter will use when + # verifying server certificates. + # If left empty, TLS uses the host's root CA set. + # emitter_ca_file: "/path/to/cert/server.pem" + + # Set to true in order to stop autodiscovery in the k8s cluster. It can be useful when running the Pod with a service account + # having limited privileges. + # Default: false + # disable_autodiscovery: false + + # Whether the emitter should skip TLS verification when submitting data. + # Default: false + # emitter_insecure_skip_verify: false + + # Histogram support is based on New Relic's guidelines for higher + # level metrics abstractions https://github.com/newrelic/newrelic-exporter-specs/blob/master/Guidelines.md. + # To better support visualization of this data, percentiles are calculated + # based on the histogram metrics and sent to New Relic. + # By default, the following percentiles are calculated: 50, 95 and 99. + # + # percentiles: + # - 50 + # - 95 + # - 99 + + transformations: [] + # - description: "Custom transformation Example" + # rename_attributes: + # - metric_prefix: "" + # attributes: + # container_name: "containerName" + # pod_name: "podName" + # namespace: "namespaceName" + # node: "nodeName" + # container: "containerName" + # pod: "podName" + # deployment: "deploymentName" + # ignore_metrics: + # # Ignore the following metrics. + # # These metrics are already collected by the New Relic Kubernetes Integration. + # - prefixes: + # - kube_daemonset_ + # - kube_deployment_ + # - kube_endpoint_ + # - kube_namespace_ + # - kube_node_ + # - kube_persistentvolume_ + # - kube_pod_ + # - kube_replicaset_ + # - kube_service_ + # - kube_statefulset_ + # copy_attributes: + # # Copy all the labels from the timeseries with metric name + # # `kube_hpa_labels` into every timeseries with a metric name that + # # starts with `kube_hpa_` only if they share the same `namespace` + # # and `hpa` labels. + # - from_metric: "kube_hpa_labels" + # to_metrics: "kube_hpa_" + # match_by: + # - namespace + # - hpa + # - from_metric: "kube_daemonset_labels" + # to_metrics: "kube_daemonset_" + # match_by: + # - namespace + # - daemonset + # - from_metric: "kube_statefulset_labels" + # to_metrics: "kube_statefulset_" + # match_by: + # - namespace + # - statefulset + # - from_metric: "kube_endpoint_labels" + # to_metrics: "kube_endpoint_" + # match_by: + # - namespace + # - endpoint + # - from_metric: "kube_service_labels" + # to_metrics: "kube_service_" + # match_by: + # - namespace + # - service + # - from_metric: "kube_node_labels" + # to_metrics: "kube_node_" + # match_by: + # - namespace + # - node + +# -- (bool) Reduces number of metrics sent in order to reduce costs. Can be configured also with `global.lowDataMode` +# @default -- false +lowDataMode: + +# -- Configures the integration to send all HTTP/HTTPS request through the proxy in that URL. The URL should have a standard format like `https://user:password@hostname:port`. Can be configured also with `global.proxy` +proxy: "" + +# -- (bool) Send the metrics to the staging backend. Requires a valid staging license key. Can be configured also with `global.nrStaging` +# @default -- false +nrStaging: +fedramp: + # fedramp.enabled -- (bool) Enables FedRAMP. Can be configured also with `global.fedramp.enabled` + # @default -- false + enabled: +# -- (bool) Sets the debug logs to this integration or all integrations if it is set globally. Can be configured also with `global.verboseLog` +# @default -- false +verboseLog: diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/Chart.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/Chart.yaml new file mode 100644 index 0000000000..a0ce0a388f --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/Chart.yaml @@ -0,0 +1,4 @@ +apiVersion: v2 +name: pixie-operator-chart +type: application +version: 0.1.6 diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/crds/olm_crd.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/crds/olm_crd.yaml new file mode 100644 index 0000000000..3f5429f789 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/crds/olm_crd.yaml @@ -0,0 +1,9045 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.0 + creationTimestamp: null + name: catalogsources.operators.coreos.com +spec: + group: operators.coreos.com + names: + categories: + - olm + kind: CatalogSource + listKind: CatalogSourceList + plural: catalogsources + shortNames: + - catsrc + singular: catalogsource + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The pretty name of the catalog + jsonPath: .spec.displayName + name: Display + type: string + - description: The type of the catalog + jsonPath: .spec.sourceType + name: Type + type: string + - description: The publisher of the catalog + jsonPath: .spec.publisher + name: Publisher + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: CatalogSource is a repository of CSVs, CRDs, and operator packages. + type: object + required: + - metadata + - spec + 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: + type: object + required: + - sourceType + properties: + address: + description: 'Address is a host that OLM can use to connect to a pre-existing registry. Format: : Only used when SourceType = SourceTypeGrpc. Ignored when the Image field is set.' + type: string + configMap: + description: ConfigMap is the name of the ConfigMap to be used to back a configmap-server registry. Only used when SourceType = SourceTypeConfigmap or SourceTypeInternal. + type: string + description: + type: string + displayName: + description: Metadata + type: string + grpcPodConfig: + description: GrpcPodConfig exposes different overrides for the pod spec of the CatalogSource Pod. Only used when SourceType = SourceTypeGrpc and Image is set. + type: object + properties: + affinity: + description: Affinity is the catalog source's pod's affinity. + type: object + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the pod. + type: object + 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. + type: array + 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). + type: object + required: + - preference + - weight + properties: + preference: + description: A node selector term, associated with the corresponding weight. + type: object + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + type: array + items: + description: A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchFields: + description: A list of node selector requirements by node's fields. + type: array + items: + description: A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + weight: + description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. + type: integer + format: int32 + 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. + type: object + required: + - nodeSelectorTerms + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The terms are ORed. + type: array + 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. + type: object + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + type: array + items: + description: A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchFields: + description: A list of node selector requirements by node's fields. + type: array + items: + description: A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). + type: object + 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. + type: array + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + type: object + required: + - podAffinityTerm + - weight + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query over a set of resources, in this case pods. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + 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. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + 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". + type: array + items: + type: string + 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 + weight: + description: weight associated with matching the corresponding podAffinityTerm, in the range 1-100. + type: integer + format: int32 + 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. + type: array + 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 + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query over a set of resources, in this case pods. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + 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. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + 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". + type: array + items: + type: string + 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 + 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)). + type: object + 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. + type: array + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + type: object + required: + - podAffinityTerm + - weight + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query over a set of resources, in this case pods. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + 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. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + 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". + type: array + items: + type: string + 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 + weight: + description: weight associated with matching the corresponding podAffinityTerm, in the range 1-100. + type: integer + format: int32 + 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. + type: array + 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 + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query over a set of resources, in this case pods. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + 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. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + 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". + type: array + items: + type: string + 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 + extractContent: + description: ExtractContent configures the gRPC catalog Pod to extract catalog metadata from the provided index image and use a well-known version of the `opm` server to expose it. The catalog index image that this CatalogSource is configured to use *must* be using the file-based catalogs in order to utilize this feature. + type: object + required: + - cacheDir + - catalogDir + properties: + cacheDir: + description: CacheDir is the directory storing the pre-calculated API cache. + type: string + catalogDir: + description: CatalogDir is the directory storing the file-based catalog contents. + type: string + memoryTarget: + description: "MemoryTarget configures the $GOMEMLIMIT value for the gRPC catalog Pod. This is a soft memory limit for the server, which the runtime will attempt to meet but makes no guarantees that it will do so. If this value is set, the Pod will have the following modifications made to the container running the server: - the $GOMEMLIMIT environment variable will be set to this value in bytes - the memory request will be set to this value \n This field should be set if it's desired to reduce the footprint of a catalog server as much as possible, or if a catalog being served is very large and needs more than the default allocation. If your index image has a file- system cache, determine a good approximation for this value by doubling the size of the package cache at /tmp/cache/cache/packages.json in the index image. \n This field is best-effort; if unset, no default will be used and no Pod memory limit or $GOMEMLIMIT value will be set." + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + nodeSelector: + description: NodeSelector is a selector which must be true for the pod to fit on a node. Selector which must match a node's labels for the pod to be scheduled on that node. + type: object + additionalProperties: + type: string + priorityClassName: + description: If specified, indicates the pod's priority. If not specified, the pod priority will be default or zero if there is no default. + type: string + securityContextConfig: + description: "SecurityContextConfig can be one of `legacy` or `restricted`. The CatalogSource's pod is either injected with the right pod.spec.securityContext and pod.spec.container[*].securityContext values to allow the pod to run in Pod Security Admission (PSA) `restricted` mode, or doesn't set these values at all, in which case the pod can only be run in PSA `baseline` or `privileged` namespaces. Currently if the SecurityContextConfig is unspecified, the default value of `legacy` is used. Specifying a value other than `legacy` or `restricted` result in a validation error. When using older catalog images, which could not be run in `restricted` mode, the SecurityContextConfig should be set to `legacy`. \n In a future version will the default will be set to `restricted`, catalog maintainers should rebuild their catalogs with a version of opm that supports running catalogSource pods in `restricted` mode to prepare for these changes. \n More information about PSA can be found here: https://kubernetes.io/docs/concepts/security/pod-security-admission/'" + type: string + default: legacy + enum: + - legacy + - restricted + tolerations: + description: Tolerations are the catalog source's pod's tolerations. + type: array + items: + description: The pod this Toleration is attached to tolerates any taint that matches the triple using the matching operator . + type: object + 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. + type: integer + format: int64 + 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 + icon: + type: object + required: + - base64data + - mediatype + properties: + base64data: + type: string + mediatype: + type: string + image: + description: Image is an operator-registry container image to instantiate a registry-server with. Only used when SourceType = SourceTypeGrpc. If present, the address field is ignored. + type: string + priority: + description: 'Priority field assigns a weight to the catalog source to prioritize them so that it can be consumed by the dependency resolver. Usage: Higher weight indicates that this catalog source is preferred over lower weighted catalog sources during dependency resolution. The range of the priority value can go from positive to negative in the range of int32. The default value to a catalog source with unassigned priority would be 0. The catalog source with the same priority values will be ranked lexicographically based on its name.' + type: integer + publisher: + type: string + secrets: + description: Secrets represent set of secrets that can be used to access the contents of the catalog. It is best to keep this list small, since each will need to be tried for every catalog entry. + type: array + items: + type: string + sourceType: + description: SourceType is the type of source + type: string + updateStrategy: + description: UpdateStrategy defines how updated catalog source images can be discovered Consists of an interval that defines polling duration and an embedded strategy type + type: object + properties: + registryPoll: + type: object + properties: + interval: + description: Interval is used to determine the time interval between checks of the latest catalog source version. The catalog operator polls to see if a new version of the catalog source is available. If available, the latest image is pulled and gRPC traffic is directed to the latest catalog source. + type: string + status: + type: object + properties: + conditions: + description: Represents the state of a CatalogSource. Note that Message and Reason represent the original status information, which may be migrated to be conditions based in the future. Any new features introduced will use conditions. + type: array + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + type: object + required: + - lastTransitionTime + - message + - reason + - status + - type + 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. + type: string + format: date-time + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + type: string + maxLength: 32768 + 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. + type: integer + format: int64 + minimum: 0 + 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. + type: string + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + status: + description: status of the condition, one of True, False, Unknown. + type: string + enum: + - "True" + - "False" + - Unknown + 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) + type: string + 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])$ + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + configMapReference: + type: object + required: + - name + - namespace + properties: + lastUpdateTime: + type: string + format: date-time + name: + type: string + namespace: + type: string + resourceVersion: + type: string + uid: + description: UID is a type that holds unique ID values, including UUIDs. Because we don't ONLY use UUIDs, this is an alias to string. Being a type captures intent and helps make sure that UIDs and names do not get conflated. + type: string + connectionState: + type: object + required: + - lastObservedState + properties: + address: + type: string + lastConnect: + type: string + format: date-time + lastObservedState: + type: string + latestImageRegistryPoll: + description: The last time the CatalogSource image registry has been polled to ensure the image is up-to-date + type: string + format: date-time + message: + description: A human readable message indicating details about why the CatalogSource is in this condition. + type: string + reason: + description: Reason is the reason the CatalogSource was transitioned to its current state. + type: string + registryService: + type: object + properties: + createdAt: + type: string + format: date-time + port: + type: string + protocol: + type: string + serviceName: + type: string + serviceNamespace: + type: string + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.0 + creationTimestamp: null + name: clusterserviceversions.operators.coreos.com +spec: + group: operators.coreos.com + names: + categories: + - olm + kind: ClusterServiceVersion + listKind: ClusterServiceVersionList + plural: clusterserviceversions + shortNames: + - csv + - csvs + singular: clusterserviceversion + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The name of the CSV + jsonPath: .spec.displayName + name: Display + type: string + - description: The version of the CSV + jsonPath: .spec.version + name: Version + type: string + - description: The name of a CSV that this one replaces + jsonPath: .spec.replaces + name: Replaces + type: string + - jsonPath: .status.phase + name: Phase + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: ClusterServiceVersion is a Custom Resource of type `ClusterServiceVersionSpec`. + type: object + required: + - metadata + - spec + 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: ClusterServiceVersionSpec declarations tell OLM how to install an operator that can manage apps for a given version. + type: object + required: + - displayName + - install + properties: + annotations: + description: Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + type: object + additionalProperties: + type: string + apiservicedefinitions: + description: APIServiceDefinitions declares all of the extension apis managed or required by an operator being ran by ClusterServiceVersion. + type: object + properties: + owned: + type: array + items: + description: APIServiceDescription provides details to OLM about apis provided via aggregation + type: object + required: + - group + - kind + - name + - version + properties: + actionDescriptors: + type: array + items: + description: ActionDescriptor describes a declarative action that can be performed on a custom resource instance + type: object + required: + - path + properties: + description: + type: string + displayName: + type: string + path: + type: string + value: + description: RawMessage is a raw encoded JSON value. It implements Marshaler and Unmarshaler and can be used to delay JSON decoding or precompute a JSON encoding. + type: string + format: byte + x-descriptors: + type: array + items: + type: string + containerPort: + type: integer + format: int32 + deploymentName: + type: string + description: + type: string + displayName: + type: string + group: + type: string + kind: + type: string + name: + type: string + resources: + type: array + items: + description: APIResourceReference is a reference to a Kubernetes resource type that the referrer utilizes. + type: object + required: + - kind + - name + - version + properties: + kind: + description: Kind of the referenced resource type. + type: string + name: + description: Plural name of the referenced resource type (CustomResourceDefinition.Spec.Names[].Plural). Empty string if the referenced resource type is not a custom resource. + type: string + version: + description: API Version of the referenced resource type. + type: string + specDescriptors: + type: array + items: + description: SpecDescriptor describes a field in a spec block of a CRD so that OLM can consume it + type: object + required: + - path + properties: + description: + type: string + displayName: + type: string + path: + type: string + value: + description: RawMessage is a raw encoded JSON value. It implements Marshaler and Unmarshaler and can be used to delay JSON decoding or precompute a JSON encoding. + type: string + format: byte + x-descriptors: + type: array + items: + type: string + statusDescriptors: + type: array + items: + description: StatusDescriptor describes a field in a status block of a CRD so that OLM can consume it + type: object + required: + - path + properties: + description: + type: string + displayName: + type: string + path: + type: string + value: + description: RawMessage is a raw encoded JSON value. It implements Marshaler and Unmarshaler and can be used to delay JSON decoding or precompute a JSON encoding. + type: string + format: byte + x-descriptors: + type: array + items: + type: string + version: + type: string + required: + type: array + items: + description: APIServiceDescription provides details to OLM about apis provided via aggregation + type: object + required: + - group + - kind + - name + - version + properties: + actionDescriptors: + type: array + items: + description: ActionDescriptor describes a declarative action that can be performed on a custom resource instance + type: object + required: + - path + properties: + description: + type: string + displayName: + type: string + path: + type: string + value: + description: RawMessage is a raw encoded JSON value. It implements Marshaler and Unmarshaler and can be used to delay JSON decoding or precompute a JSON encoding. + type: string + format: byte + x-descriptors: + type: array + items: + type: string + containerPort: + type: integer + format: int32 + deploymentName: + type: string + description: + type: string + displayName: + type: string + group: + type: string + kind: + type: string + name: + type: string + resources: + type: array + items: + description: APIResourceReference is a reference to a Kubernetes resource type that the referrer utilizes. + type: object + required: + - kind + - name + - version + properties: + kind: + description: Kind of the referenced resource type. + type: string + name: + description: Plural name of the referenced resource type (CustomResourceDefinition.Spec.Names[].Plural). Empty string if the referenced resource type is not a custom resource. + type: string + version: + description: API Version of the referenced resource type. + type: string + specDescriptors: + type: array + items: + description: SpecDescriptor describes a field in a spec block of a CRD so that OLM can consume it + type: object + required: + - path + properties: + description: + type: string + displayName: + type: string + path: + type: string + value: + description: RawMessage is a raw encoded JSON value. It implements Marshaler and Unmarshaler and can be used to delay JSON decoding or precompute a JSON encoding. + type: string + format: byte + x-descriptors: + type: array + items: + type: string + statusDescriptors: + type: array + items: + description: StatusDescriptor describes a field in a status block of a CRD so that OLM can consume it + type: object + required: + - path + properties: + description: + type: string + displayName: + type: string + path: + type: string + value: + description: RawMessage is a raw encoded JSON value. It implements Marshaler and Unmarshaler and can be used to delay JSON decoding or precompute a JSON encoding. + type: string + format: byte + x-descriptors: + type: array + items: + type: string + version: + type: string + cleanup: + description: Cleanup specifies the cleanup behaviour when the CSV gets deleted + type: object + required: + - enabled + properties: + enabled: + type: boolean + customresourcedefinitions: + description: "CustomResourceDefinitions declares all of the CRDs managed or required by an operator being ran by ClusterServiceVersion. \n If the CRD is present in the Owned list, it is implicitly required." + type: object + properties: + owned: + type: array + items: + description: CRDDescription provides details to OLM about the CRDs + type: object + required: + - kind + - name + - version + properties: + actionDescriptors: + type: array + items: + description: ActionDescriptor describes a declarative action that can be performed on a custom resource instance + type: object + required: + - path + properties: + description: + type: string + displayName: + type: string + path: + type: string + value: + description: RawMessage is a raw encoded JSON value. It implements Marshaler and Unmarshaler and can be used to delay JSON decoding or precompute a JSON encoding. + type: string + format: byte + x-descriptors: + type: array + items: + type: string + description: + type: string + displayName: + type: string + kind: + type: string + name: + type: string + resources: + type: array + items: + description: APIResourceReference is a reference to a Kubernetes resource type that the referrer utilizes. + type: object + required: + - kind + - name + - version + properties: + kind: + description: Kind of the referenced resource type. + type: string + name: + description: Plural name of the referenced resource type (CustomResourceDefinition.Spec.Names[].Plural). Empty string if the referenced resource type is not a custom resource. + type: string + version: + description: API Version of the referenced resource type. + type: string + specDescriptors: + type: array + items: + description: SpecDescriptor describes a field in a spec block of a CRD so that OLM can consume it + type: object + required: + - path + properties: + description: + type: string + displayName: + type: string + path: + type: string + value: + description: RawMessage is a raw encoded JSON value. It implements Marshaler and Unmarshaler and can be used to delay JSON decoding or precompute a JSON encoding. + type: string + format: byte + x-descriptors: + type: array + items: + type: string + statusDescriptors: + type: array + items: + description: StatusDescriptor describes a field in a status block of a CRD so that OLM can consume it + type: object + required: + - path + properties: + description: + type: string + displayName: + type: string + path: + type: string + value: + description: RawMessage is a raw encoded JSON value. It implements Marshaler and Unmarshaler and can be used to delay JSON decoding or precompute a JSON encoding. + type: string + format: byte + x-descriptors: + type: array + items: + type: string + version: + type: string + required: + type: array + items: + description: CRDDescription provides details to OLM about the CRDs + type: object + required: + - kind + - name + - version + properties: + actionDescriptors: + type: array + items: + description: ActionDescriptor describes a declarative action that can be performed on a custom resource instance + type: object + required: + - path + properties: + description: + type: string + displayName: + type: string + path: + type: string + value: + description: RawMessage is a raw encoded JSON value. It implements Marshaler and Unmarshaler and can be used to delay JSON decoding or precompute a JSON encoding. + type: string + format: byte + x-descriptors: + type: array + items: + type: string + description: + type: string + displayName: + type: string + kind: + type: string + name: + type: string + resources: + type: array + items: + description: APIResourceReference is a reference to a Kubernetes resource type that the referrer utilizes. + type: object + required: + - kind + - name + - version + properties: + kind: + description: Kind of the referenced resource type. + type: string + name: + description: Plural name of the referenced resource type (CustomResourceDefinition.Spec.Names[].Plural). Empty string if the referenced resource type is not a custom resource. + type: string + version: + description: API Version of the referenced resource type. + type: string + specDescriptors: + type: array + items: + description: SpecDescriptor describes a field in a spec block of a CRD so that OLM can consume it + type: object + required: + - path + properties: + description: + type: string + displayName: + type: string + path: + type: string + value: + description: RawMessage is a raw encoded JSON value. It implements Marshaler and Unmarshaler and can be used to delay JSON decoding or precompute a JSON encoding. + type: string + format: byte + x-descriptors: + type: array + items: + type: string + statusDescriptors: + type: array + items: + description: StatusDescriptor describes a field in a status block of a CRD so that OLM can consume it + type: object + required: + - path + properties: + description: + type: string + displayName: + type: string + path: + type: string + value: + description: RawMessage is a raw encoded JSON value. It implements Marshaler and Unmarshaler and can be used to delay JSON decoding or precompute a JSON encoding. + type: string + format: byte + x-descriptors: + type: array + items: + type: string + version: + type: string + description: + description: Description of the operator. Can include the features, limitations or use-cases of the operator. + type: string + displayName: + description: The name of the operator in display format. + type: string + icon: + description: The icon for this operator. + type: array + items: + type: object + required: + - base64data + - mediatype + properties: + base64data: + type: string + mediatype: + type: string + install: + description: NamedInstallStrategy represents the block of an ClusterServiceVersion resource where the install strategy is specified. + type: object + required: + - strategy + properties: + spec: + description: StrategyDetailsDeployment represents the parsed details of a Deployment InstallStrategy. + type: object + required: + - deployments + properties: + clusterPermissions: + type: array + items: + description: StrategyDeploymentPermissions describe the rbac rules and service account needed by the install strategy + type: object + required: + - rules + - serviceAccountName + properties: + rules: + type: array + items: + description: PolicyRule holds information that describes a policy rule, but does not contain information about who the rule applies to or which namespace the rule applies to. + type: object + required: + - verbs + properties: + apiGroups: + description: APIGroups is the name of the APIGroup that contains the resources. If multiple API groups are specified, any action requested against one of the enumerated resources in any API group will be allowed. "" represents the core API group and "*" represents all API groups. + type: array + items: + type: string + nonResourceURLs: + description: NonResourceURLs is a set of partial urls that a user should have access to. *s are allowed, but only as the full, final step in the path Since non-resource URLs are not namespaced, this field is only applicable for ClusterRoles referenced from a ClusterRoleBinding. Rules can either apply to API resources (such as "pods" or "secrets") or non-resource URL paths (such as "/api"), but not both. + type: array + items: + type: string + resourceNames: + description: ResourceNames is an optional white list of names that the rule applies to. An empty set means that everything is allowed. + type: array + items: + type: string + resources: + description: Resources is a list of resources this rule applies to. '*' represents all resources. + type: array + items: + type: string + verbs: + description: Verbs is a list of Verbs that apply to ALL the ResourceKinds contained in this rule. '*' represents all verbs. + type: array + items: + type: string + serviceAccountName: + type: string + deployments: + type: array + items: + description: StrategyDeploymentSpec contains the name, spec and labels for the deployment ALM should create + type: object + required: + - name + - spec + properties: + label: + description: Set is a map of label:value. It implements Labels. + type: object + additionalProperties: + type: string + name: + type: string + spec: + description: DeploymentSpec is the specification of the desired behavior of the Deployment. + type: object + required: + - selector + - template + properties: + minReadySeconds: + description: Minimum number of seconds for which a newly created pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready) + type: integer + format: int32 + paused: + description: Indicates that the deployment is paused. + type: boolean + progressDeadlineSeconds: + description: The maximum time in seconds for a deployment to make progress before it is considered to be failed. The deployment controller will continue to process failed deployments and a condition with a ProgressDeadlineExceeded reason will be surfaced in the deployment status. Note that progress will not be estimated during the time a deployment is paused. Defaults to 600s. + type: integer + format: int32 + replicas: + description: Number of desired pods. This is a pointer to distinguish between explicit zero and not specified. Defaults to 1. + type: integer + format: int32 + revisionHistoryLimit: + description: The number of old ReplicaSets to retain to allow rollback. This is a pointer to distinguish between explicit zero and not specified. Defaults to 10. + type: integer + format: int32 + selector: + description: Label selector for pods. Existing ReplicaSets whose pods are selected by this will be the ones affected by this deployment. It must match the pod template's labels. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + strategy: + description: The deployment strategy to use to replace existing pods with new ones. + type: object + properties: + rollingUpdate: + description: 'Rolling update config params. Present only if DeploymentStrategyType = RollingUpdate. --- TODO: Update this to follow our convention for oneOf, whatever we decide it to be.' + type: object + properties: + maxSurge: + description: 'The maximum number of pods that can be scheduled above the desired number of pods. Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). This can not be 0 if MaxUnavailable is 0. Absolute number is calculated from percentage by rounding up. Defaults to 25%. Example: when this is set to 30%, the new ReplicaSet can be scaled up immediately when the rolling update starts, such that the total number of old and new pods do not exceed 130% of desired pods. Once old pods have been killed, new ReplicaSet can be scaled up further, ensuring that total number of pods running at any time during the update is at most 130% of desired pods.' + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + maxUnavailable: + description: 'The maximum number of pods that can be unavailable during the update. Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). Absolute number is calculated from percentage by rounding down. This can not be 0 if MaxSurge is 0. Defaults to 25%. Example: when this is set to 30%, the old ReplicaSet can be scaled down to 70% of desired pods immediately when the rolling update starts. Once new pods are ready, old ReplicaSet can be scaled down further, followed by scaling up the new ReplicaSet, ensuring that the total number of pods available at all times during the update is at least 70% of desired pods.' + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + type: + description: Type of deployment. Can be "Recreate" or "RollingUpdate". Default is RollingUpdate. + type: string + template: + description: Template describes the pods that will be created. The only allowed template.spec.restartPolicy value is "Always". + type: object + properties: + metadata: + description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' + type: object + x-kubernetes-preserve-unknown-fields: true + spec: + description: 'Specification of the desired behavior of the pod. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' + type: object + required: + - containers + properties: + activeDeadlineSeconds: + description: Optional duration in seconds the pod may be active on the node relative to StartTime before the system will actively try to mark it failed and kill associated containers. Value must be a positive integer. + type: integer + format: int64 + affinity: + description: If specified, the pod's scheduling constraints + type: object + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the pod. + type: object + 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. + type: array + 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). + type: object + required: + - preference + - weight + properties: + preference: + description: A node selector term, associated with the corresponding weight. + type: object + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + type: array + items: + description: A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchFields: + description: A list of node selector requirements by node's fields. + type: array + items: + description: A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + weight: + description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. + type: integer + format: int32 + 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. + type: object + required: + - nodeSelectorTerms + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The terms are ORed. + type: array + 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. + type: object + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + type: array + items: + description: A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchFields: + description: A list of node selector requirements by node's fields. + type: array + items: + description: A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). + type: object + 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. + type: array + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + type: object + required: + - podAffinityTerm + - weight + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query over a set of resources, in this case pods. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + 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. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + 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". + type: array + items: + type: string + 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 + weight: + description: weight associated with matching the corresponding podAffinityTerm, in the range 1-100. + type: integer + format: int32 + 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. + type: array + 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 + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query over a set of resources, in this case pods. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + 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. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + 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". + type: array + items: + type: string + 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 + 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)). + type: object + 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. + type: array + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + type: object + required: + - podAffinityTerm + - weight + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query over a set of resources, in this case pods. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + 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. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + 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". + type: array + items: + type: string + 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 + weight: + description: weight associated with matching the corresponding podAffinityTerm, in the range 1-100. + type: integer + format: int32 + 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. + type: array + 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 + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query over a set of resources, in this case pods. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + 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. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + 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". + type: array + items: + type: string + 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 + automountServiceAccountToken: + description: AutomountServiceAccountToken indicates whether a service account token should be automatically mounted. + type: boolean + containers: + description: List of containers belonging to the pod. Containers cannot currently be added or removed. There must be at least one container in a Pod. Cannot be updated. + type: array + items: + description: A single application container that you want to run within a pod. + type: object + required: + - name + properties: + args: + description: 'Arguments to the entrypoint. The container image''s CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container''s environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + type: array + items: + type: string + command: + description: 'Entrypoint array. Not executed within a shell. The container image''s ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container''s environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + type: array + items: + type: string + env: + description: List of environment variables to set in the container. Cannot be updated. + type: array + items: + description: EnvVar represents an environment variable present in a Container. + type: object + required: + - name + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's value. Cannot be used if value is not empty. + type: object + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + type: object + required: + - key + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap or its key must be defined + type: boolean + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs.' + type: object + required: + - fieldPath + properties: + apiVersion: + description: Version of the schema the FieldPath is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified API version. + type: string + resourceFieldRef: + description: 'Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported.' + type: object + required: + - resource + properties: + containerName: + description: 'Container name: required for volumes, optional for env vars' + type: string + divisor: + description: Specifies the output format of the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + type: object + required: + - key + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret or its key must be defined + type: boolean + envFrom: + description: List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated. + type: array + items: + description: EnvFromSource represents the source of a set of ConfigMaps + type: object + properties: + configMapRef: + description: The ConfigMap to select from + type: object + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap must be defined + type: boolean + prefix: + description: An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + type: object + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + image: + description: 'Container image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.' + type: string + imagePullPolicy: + description: 'Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' + type: string + lifecycle: + description: Actions that the management system should take in response to container lifecycle events. Cannot be updated. + type: object + properties: + postStart: + description: 'PostStart is called immediately after a container is created. If the handler fails, the container is terminated and restarted according to its restart policy. Other management of the container blocks until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + type: object + properties: + exec: + description: Exec specifies the action to take. + type: object + properties: + command: + description: Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + httpGet: + description: HTTPGet specifies the http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + path: + description: Path to access on the HTTP server. + type: string + port: + description: Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. Defaults to HTTP. + type: string + tcpSocket: + description: Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept for the backward compatibility. There are no validation of this field and lifecycle hooks will fail in runtime when tcp handler is specified. + type: object + required: + - port + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + preStop: + description: 'PreStop is called immediately before a container is terminated due to an API request or management event such as liveness/startup probe failure, preemption, resource contention, etc. The handler is not called if the container crashes or exits. The Pod''s termination grace period countdown begins before the PreStop hook is executed. Regardless of the outcome of the handler, the container will eventually terminate within the Pod''s termination grace period (unless delayed by finalizers). Other management of the container blocks until the hook completes or until the termination grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + type: object + properties: + exec: + description: Exec specifies the action to take. + type: object + properties: + command: + description: Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + httpGet: + description: HTTPGet specifies the http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + path: + description: Path to access on the HTTP server. + type: string + port: + description: Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. Defaults to HTTP. + type: string + tcpSocket: + description: Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept for the backward compatibility. There are no validation of this field and lifecycle hooks will fail in runtime when tcp handler is specified. + type: object + required: + - port + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + livenessProbe: + description: 'Periodic probe of container liveness. Container will be restarted if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: object + properties: + exec: + description: Exec specifies the action to take. + type: object + properties: + command: + description: Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + failureThreshold: + description: Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1. + type: integer + format: int32 + grpc: + description: GRPC specifies an action involving a GRPC port. + type: object + required: + - port + properties: + port: + description: Port number of the gRPC service. Number must be in the range 1 to 65535. + type: integer + format: int32 + service: + description: "Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). \n If this is not specified, the default behavior is defined by gRPC." + type: string + httpGet: + description: HTTPGet specifies the http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + path: + description: Path to access on the HTTP server. + type: string + port: + description: Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. Defaults to HTTP. + type: string + initialDelaySeconds: + description: 'Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + periodSeconds: + description: How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1. + type: integer + format: int32 + successThreshold: + description: Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + type: integer + format: int32 + tcpSocket: + description: TCPSocket specifies an action involving a TCP port. + type: object + required: + - port + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + type: integer + format: int64 + timeoutSeconds: + description: 'Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + name: + description: Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated. + type: string + ports: + description: List of ports to expose from the container. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default "0.0.0.0" address inside a container will be accessible from the network. Modifying this array with strategic merge patch may corrupt the data. For more information See https://github.com/kubernetes/kubernetes/issues/108255. Cannot be updated. + type: array + items: + description: ContainerPort represents a network port in a single container. + type: object + required: + - containerPort + properties: + containerPort: + description: Number of port to expose on the pod's IP address. This must be a valid port number, 0 < x < 65536. + type: integer + format: int32 + hostIP: + description: What host IP to bind the external port to. + type: string + hostPort: + description: Number of port to expose on the host. If specified, this must be a valid port number, 0 < x < 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this. + type: integer + format: int32 + name: + description: If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services. + type: string + protocol: + description: Protocol for port. Must be UDP, TCP, or SCTP. Defaults to "TCP". + type: string + default: TCP + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: 'Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: object + properties: + exec: + description: Exec specifies the action to take. + type: object + properties: + command: + description: Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + failureThreshold: + description: Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1. + type: integer + format: int32 + grpc: + description: GRPC specifies an action involving a GRPC port. + type: object + required: + - port + properties: + port: + description: Port number of the gRPC service. Number must be in the range 1 to 65535. + type: integer + format: int32 + service: + description: "Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). \n If this is not specified, the default behavior is defined by gRPC." + type: string + httpGet: + description: HTTPGet specifies the http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + path: + description: Path to access on the HTTP server. + type: string + port: + description: Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. Defaults to HTTP. + type: string + initialDelaySeconds: + description: 'Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + periodSeconds: + description: How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1. + type: integer + format: int32 + successThreshold: + description: Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + type: integer + format: int32 + tcpSocket: + description: TCPSocket specifies an action involving a TCP port. + type: object + required: + - port + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + type: integer + format: int64 + timeoutSeconds: + description: 'Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + resizePolicy: + description: Resources resize policy for the container. + type: array + items: + description: ContainerResizePolicy represents resource resize policy for the container. + type: object + required: + - resourceName + - restartPolicy + properties: + resourceName: + description: 'Name of the resource to which this resource resize policy applies. Supported values: cpu, memory.' + type: string + restartPolicy: + description: Restart policy to apply when specified resource is resized. If not specified, it defaults to NotRequired. + type: string + x-kubernetes-list-type: atomic + resources: + description: 'Compute Resources required by this container. Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + properties: + claims: + description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." + type: array + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + type: object + required: + - name + properties: + name: + description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. + type: string + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + additionalProperties: + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + requests: + description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + additionalProperties: + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + restartPolicy: + description: 'RestartPolicy defines the restart behavior of individual containers in a pod. This field may only be set for init containers, and the only allowed value is "Always". For non-init containers or when this field is not specified, the restart behavior is defined by the Pod''s restart policy and the container type. Setting the RestartPolicy as "Always" for the init container will have the following effect: this init container will be continually restarted on exit until all regular containers have terminated. Once all regular containers have completed, all init containers with restartPolicy "Always" will be shut down. This lifecycle differs from normal init containers and is often referred to as a "sidecar" container. Although this init container still starts in the init container sequence, it does not wait for the container to complete before proceeding to the next init container. Instead, the next init container starts immediately after this init container is started, or after any startupProbe has successfully completed.' + type: string + securityContext: + description: 'SecurityContext defines the security options the container should be run with. If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' + type: object + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN Note that this field cannot be set when spec.os.name is windows.' + type: boolean + capabilities: + description: The capabilities to add/drop when running containers. Defaults to the default set of capabilities granted by the container runtime. Note that this field cannot be set when spec.os.name is windows. + type: object + properties: + add: + description: Added capabilities + type: array + items: + description: Capability represent POSIX capabilities type + type: string + drop: + description: Removed capabilities + type: array + items: + description: Capability represent POSIX capabilities type + type: string + privileged: + description: Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false. Note that this field cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled. Note that this field cannot be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only root filesystem. Default is false. Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows. + type: integer + format: int64 + runAsNonRoot: + description: Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows. + type: integer + format: int64 + seLinuxOptions: + description: The SELinux context to be applied to the container. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows. + type: object + properties: + level: + description: Level is SELinux level label that applies to the container. + type: string + role: + description: Role is a SELinux role label that applies to the container. + type: string + type: + description: Type is a SELinux type label that applies to the container. + type: string + user: + description: User is a SELinux user label that applies to the container. + type: string + seccompProfile: + description: The seccomp options to use by this container. If seccomp options are provided at both the pod & container level, the container options override the pod options. Note that this field cannot be set when spec.os.name is windows. + type: object + required: + - type + properties: + localhostProfile: + description: localhostProfile indicates a profile defined in a file on the node should be used. The profile must be preconfigured on the node to work. Must be a descending path, relative to the kubelet's configured seccomp profile location. Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: "type indicates which kind of seccomp profile will be applied. Valid options are: \n Localhost - a profile defined in a file on the node should be used. RuntimeDefault - the container runtime default profile should be used. Unconfined - no profile should be applied." + type: string + windowsOptions: + description: The Windows specific settings applied to all containers. If unspecified, the options from the PodSecurityContext will be used. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is linux. + type: object + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should be run as a 'Host Process' container. All of a Pod's containers must have the same effective HostProcess value (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + startupProbe: + description: 'StartupProbe indicates that the Pod has successfully initialized. If specified, no other probes are executed until this completes successfully. If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. This can be used to provide different probe parameters at the beginning of a Pod''s lifecycle, when it might take a long time to load data or warm a cache, than during steady-state operation. This cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: object + properties: + exec: + description: Exec specifies the action to take. + type: object + properties: + command: + description: Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + failureThreshold: + description: Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1. + type: integer + format: int32 + grpc: + description: GRPC specifies an action involving a GRPC port. + type: object + required: + - port + properties: + port: + description: Port number of the gRPC service. Number must be in the range 1 to 65535. + type: integer + format: int32 + service: + description: "Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). \n If this is not specified, the default behavior is defined by gRPC." + type: string + httpGet: + description: HTTPGet specifies the http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + path: + description: Path to access on the HTTP server. + type: string + port: + description: Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. Defaults to HTTP. + type: string + initialDelaySeconds: + description: 'Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + periodSeconds: + description: How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1. + type: integer + format: int32 + successThreshold: + description: Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + type: integer + format: int32 + tcpSocket: + description: TCPSocket specifies an action involving a TCP port. + type: object + required: + - port + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + type: integer + format: int64 + timeoutSeconds: + description: 'Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + stdin: + description: Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false. + type: boolean + stdinOnce: + description: Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false + type: boolean + terminationMessagePath: + description: 'Optional: Path at which the file to which the container''s termination message will be written is mounted into the container''s filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.' + type: string + terminationMessagePolicy: + description: Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated. + type: string + tty: + description: Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices to be used by the container. + type: array + items: + description: volumeDevice describes a mapping of a raw block device within a container. + type: object + required: + - devicePath + - name + properties: + devicePath: + description: devicePath is the path inside of the container that the device will be mapped to. + type: string + name: + description: name must match the name of a persistentVolumeClaim in the pod + type: string + volumeMounts: + description: Pod volumes to mount into the container's filesystem. Cannot be updated. + type: array + items: + description: VolumeMount describes a mounting of a Volume within a container. + type: object + required: + - mountPath + - name + properties: + mountPath: + description: Path within the container at which the volume should be mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false. + type: boolean + subPath: + description: Path within the volume from which the container's volume should be mounted. Defaults to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume from which the container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to "" (volume's root). SubPathExpr and SubPath are mutually exclusive. + type: string + workingDir: + description: Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated. + type: string + dnsConfig: + description: Specifies the DNS parameters of a pod. Parameters specified here will be merged to the generated DNS configuration based on DNSPolicy. + type: object + properties: + nameservers: + description: A list of DNS name server IP addresses. This will be appended to the base nameservers generated from DNSPolicy. Duplicated nameservers will be removed. + type: array + items: + type: string + options: + description: A list of DNS resolver options. This will be merged with the base options generated from DNSPolicy. Duplicated entries will be removed. Resolution options given in Options will override those that appear in the base DNSPolicy. + type: array + items: + description: PodDNSConfigOption defines DNS resolver options of a pod. + type: object + properties: + name: + description: Required. + type: string + value: + type: string + searches: + description: A list of DNS search domains for host-name lookup. This will be appended to the base search paths generated from DNSPolicy. Duplicated search paths will be removed. + type: array + items: + type: string + dnsPolicy: + description: Set DNS policy for the pod. Defaults to "ClusterFirst". Valid values are 'ClusterFirstWithHostNet', 'ClusterFirst', 'Default' or 'None'. DNS parameters given in DNSConfig will be merged with the policy selected with DNSPolicy. To have DNS options set along with hostNetwork, you have to specify DNS policy explicitly to 'ClusterFirstWithHostNet'. + type: string + enableServiceLinks: + description: 'EnableServiceLinks indicates whether information about services should be injected into pod''s environment variables, matching the syntax of Docker links. Optional: Defaults to true.' + type: boolean + ephemeralContainers: + description: List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. + type: array + items: + description: "An EphemeralContainer is a temporary container that you may add to an existing Pod for user-initiated activities such as debugging. Ephemeral containers have no resource or scheduling guarantees, and they will not be restarted when they exit or when a Pod is removed or restarted. The kubelet may evict a Pod if an ephemeral container causes the Pod to exceed its resource allocation. \n To add an ephemeral container, use the ephemeralcontainers subresource of an existing Pod. Ephemeral containers may not be removed or restarted." + type: object + required: + - name + properties: + args: + description: 'Arguments to the entrypoint. The image''s CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container''s environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + type: array + items: + type: string + command: + description: 'Entrypoint array. Not executed within a shell. The image''s ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container''s environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + type: array + items: + type: string + env: + description: List of environment variables to set in the container. Cannot be updated. + type: array + items: + description: EnvVar represents an environment variable present in a Container. + type: object + required: + - name + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's value. Cannot be used if value is not empty. + type: object + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + type: object + required: + - key + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap or its key must be defined + type: boolean + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs.' + type: object + required: + - fieldPath + properties: + apiVersion: + description: Version of the schema the FieldPath is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified API version. + type: string + resourceFieldRef: + description: 'Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported.' + type: object + required: + - resource + properties: + containerName: + description: 'Container name: required for volumes, optional for env vars' + type: string + divisor: + description: Specifies the output format of the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + type: object + required: + - key + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret or its key must be defined + type: boolean + envFrom: + description: List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated. + type: array + items: + description: EnvFromSource represents the source of a set of ConfigMaps + type: object + properties: + configMapRef: + description: The ConfigMap to select from + type: object + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap must be defined + type: boolean + prefix: + description: An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + type: object + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + image: + description: 'Container image name. More info: https://kubernetes.io/docs/concepts/containers/images' + type: string + imagePullPolicy: + description: 'Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' + type: string + lifecycle: + description: Lifecycle is not allowed for ephemeral containers. + type: object + properties: + postStart: + description: 'PostStart is called immediately after a container is created. If the handler fails, the container is terminated and restarted according to its restart policy. Other management of the container blocks until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + type: object + properties: + exec: + description: Exec specifies the action to take. + type: object + properties: + command: + description: Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + httpGet: + description: HTTPGet specifies the http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + path: + description: Path to access on the HTTP server. + type: string + port: + description: Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. Defaults to HTTP. + type: string + tcpSocket: + description: Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept for the backward compatibility. There are no validation of this field and lifecycle hooks will fail in runtime when tcp handler is specified. + type: object + required: + - port + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + preStop: + description: 'PreStop is called immediately before a container is terminated due to an API request or management event such as liveness/startup probe failure, preemption, resource contention, etc. The handler is not called if the container crashes or exits. The Pod''s termination grace period countdown begins before the PreStop hook is executed. Regardless of the outcome of the handler, the container will eventually terminate within the Pod''s termination grace period (unless delayed by finalizers). Other management of the container blocks until the hook completes or until the termination grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + type: object + properties: + exec: + description: Exec specifies the action to take. + type: object + properties: + command: + description: Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + httpGet: + description: HTTPGet specifies the http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + path: + description: Path to access on the HTTP server. + type: string + port: + description: Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. Defaults to HTTP. + type: string + tcpSocket: + description: Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept for the backward compatibility. There are no validation of this field and lifecycle hooks will fail in runtime when tcp handler is specified. + type: object + required: + - port + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + livenessProbe: + description: Probes are not allowed for ephemeral containers. + type: object + properties: + exec: + description: Exec specifies the action to take. + type: object + properties: + command: + description: Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + failureThreshold: + description: Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1. + type: integer + format: int32 + grpc: + description: GRPC specifies an action involving a GRPC port. + type: object + required: + - port + properties: + port: + description: Port number of the gRPC service. Number must be in the range 1 to 65535. + type: integer + format: int32 + service: + description: "Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). \n If this is not specified, the default behavior is defined by gRPC." + type: string + httpGet: + description: HTTPGet specifies the http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + path: + description: Path to access on the HTTP server. + type: string + port: + description: Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. Defaults to HTTP. + type: string + initialDelaySeconds: + description: 'Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + periodSeconds: + description: How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1. + type: integer + format: int32 + successThreshold: + description: Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + type: integer + format: int32 + tcpSocket: + description: TCPSocket specifies an action involving a TCP port. + type: object + required: + - port + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + type: integer + format: int64 + timeoutSeconds: + description: 'Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + name: + description: Name of the ephemeral container specified as a DNS_LABEL. This name must be unique among all containers, init containers and ephemeral containers. + type: string + ports: + description: Ports are not allowed for ephemeral containers. + type: array + items: + description: ContainerPort represents a network port in a single container. + type: object + required: + - containerPort + properties: + containerPort: + description: Number of port to expose on the pod's IP address. This must be a valid port number, 0 < x < 65536. + type: integer + format: int32 + hostIP: + description: What host IP to bind the external port to. + type: string + hostPort: + description: Number of port to expose on the host. If specified, this must be a valid port number, 0 < x < 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this. + type: integer + format: int32 + name: + description: If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services. + type: string + protocol: + description: Protocol for port. Must be UDP, TCP, or SCTP. Defaults to "TCP". + type: string + default: TCP + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: Probes are not allowed for ephemeral containers. + type: object + properties: + exec: + description: Exec specifies the action to take. + type: object + properties: + command: + description: Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + failureThreshold: + description: Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1. + type: integer + format: int32 + grpc: + description: GRPC specifies an action involving a GRPC port. + type: object + required: + - port + properties: + port: + description: Port number of the gRPC service. Number must be in the range 1 to 65535. + type: integer + format: int32 + service: + description: "Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). \n If this is not specified, the default behavior is defined by gRPC." + type: string + httpGet: + description: HTTPGet specifies the http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + path: + description: Path to access on the HTTP server. + type: string + port: + description: Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. Defaults to HTTP. + type: string + initialDelaySeconds: + description: 'Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + periodSeconds: + description: How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1. + type: integer + format: int32 + successThreshold: + description: Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + type: integer + format: int32 + tcpSocket: + description: TCPSocket specifies an action involving a TCP port. + type: object + required: + - port + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + type: integer + format: int64 + timeoutSeconds: + description: 'Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + resizePolicy: + description: Resources resize policy for the container. + type: array + items: + description: ContainerResizePolicy represents resource resize policy for the container. + type: object + required: + - resourceName + - restartPolicy + properties: + resourceName: + description: 'Name of the resource to which this resource resize policy applies. Supported values: cpu, memory.' + type: string + restartPolicy: + description: Restart policy to apply when specified resource is resized. If not specified, it defaults to NotRequired. + type: string + x-kubernetes-list-type: atomic + resources: + description: Resources are not allowed for ephemeral containers. Ephemeral containers use spare resources already allocated to the pod. + type: object + properties: + claims: + description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." + type: array + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + type: object + required: + - name + properties: + name: + description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. + type: string + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + additionalProperties: + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + requests: + description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + additionalProperties: + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + restartPolicy: + description: Restart policy for the container to manage the restart behavior of each container within a pod. This may only be set for init containers. You cannot set this field on ephemeral containers. + type: string + securityContext: + description: 'Optional: SecurityContext defines the security options the ephemeral container should be run with. If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext.' + type: object + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN Note that this field cannot be set when spec.os.name is windows.' + type: boolean + capabilities: + description: The capabilities to add/drop when running containers. Defaults to the default set of capabilities granted by the container runtime. Note that this field cannot be set when spec.os.name is windows. + type: object + properties: + add: + description: Added capabilities + type: array + items: + description: Capability represent POSIX capabilities type + type: string + drop: + description: Removed capabilities + type: array + items: + description: Capability represent POSIX capabilities type + type: string + privileged: + description: Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false. Note that this field cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled. Note that this field cannot be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only root filesystem. Default is false. Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows. + type: integer + format: int64 + runAsNonRoot: + description: Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows. + type: integer + format: int64 + seLinuxOptions: + description: The SELinux context to be applied to the container. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows. + type: object + properties: + level: + description: Level is SELinux level label that applies to the container. + type: string + role: + description: Role is a SELinux role label that applies to the container. + type: string + type: + description: Type is a SELinux type label that applies to the container. + type: string + user: + description: User is a SELinux user label that applies to the container. + type: string + seccompProfile: + description: The seccomp options to use by this container. If seccomp options are provided at both the pod & container level, the container options override the pod options. Note that this field cannot be set when spec.os.name is windows. + type: object + required: + - type + properties: + localhostProfile: + description: localhostProfile indicates a profile defined in a file on the node should be used. The profile must be preconfigured on the node to work. Must be a descending path, relative to the kubelet's configured seccomp profile location. Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: "type indicates which kind of seccomp profile will be applied. Valid options are: \n Localhost - a profile defined in a file on the node should be used. RuntimeDefault - the container runtime default profile should be used. Unconfined - no profile should be applied." + type: string + windowsOptions: + description: The Windows specific settings applied to all containers. If unspecified, the options from the PodSecurityContext will be used. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is linux. + type: object + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should be run as a 'Host Process' container. All of a Pod's containers must have the same effective HostProcess value (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + startupProbe: + description: Probes are not allowed for ephemeral containers. + type: object + properties: + exec: + description: Exec specifies the action to take. + type: object + properties: + command: + description: Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + failureThreshold: + description: Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1. + type: integer + format: int32 + grpc: + description: GRPC specifies an action involving a GRPC port. + type: object + required: + - port + properties: + port: + description: Port number of the gRPC service. Number must be in the range 1 to 65535. + type: integer + format: int32 + service: + description: "Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). \n If this is not specified, the default behavior is defined by gRPC." + type: string + httpGet: + description: HTTPGet specifies the http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + path: + description: Path to access on the HTTP server. + type: string + port: + description: Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. Defaults to HTTP. + type: string + initialDelaySeconds: + description: 'Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + periodSeconds: + description: How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1. + type: integer + format: int32 + successThreshold: + description: Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + type: integer + format: int32 + tcpSocket: + description: TCPSocket specifies an action involving a TCP port. + type: object + required: + - port + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + type: integer + format: int64 + timeoutSeconds: + description: 'Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + stdin: + description: Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false. + type: boolean + stdinOnce: + description: Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false + type: boolean + targetContainerName: + description: "If set, the name of the container from PodSpec that this ephemeral container targets. The ephemeral container will be run in the namespaces (IPC, PID, etc) of this container. If not set then the ephemeral container uses the namespaces configured in the Pod spec. \n The container runtime must implement support for this feature. If the runtime does not support namespace targeting then the result of setting this field is undefined." + type: string + terminationMessagePath: + description: 'Optional: Path at which the file to which the container''s termination message will be written is mounted into the container''s filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.' + type: string + terminationMessagePolicy: + description: Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated. + type: string + tty: + description: Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices to be used by the container. + type: array + items: + description: volumeDevice describes a mapping of a raw block device within a container. + type: object + required: + - devicePath + - name + properties: + devicePath: + description: devicePath is the path inside of the container that the device will be mapped to. + type: string + name: + description: name must match the name of a persistentVolumeClaim in the pod + type: string + volumeMounts: + description: Pod volumes to mount into the container's filesystem. Subpath mounts are not allowed for ephemeral containers. Cannot be updated. + type: array + items: + description: VolumeMount describes a mounting of a Volume within a container. + type: object + required: + - mountPath + - name + properties: + mountPath: + description: Path within the container at which the volume should be mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false. + type: boolean + subPath: + description: Path within the volume from which the container's volume should be mounted. Defaults to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume from which the container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to "" (volume's root). SubPathExpr and SubPath are mutually exclusive. + type: string + workingDir: + description: Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated. + type: string + hostAliases: + description: HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods. + type: array + items: + description: HostAlias holds the mapping between IP and hostnames that will be injected as an entry in the pod's hosts file. + type: object + properties: + hostnames: + description: Hostnames for the above IP address. + type: array + items: + type: string + ip: + description: IP address of the host file entry. + type: string + hostIPC: + description: 'Use the host''s ipc namespace. Optional: Default to false.' + type: boolean + hostNetwork: + description: Host networking requested for this pod. Use the host's network namespace. If this option is set, the ports that will be used must be specified. Default to false. + type: boolean + hostPID: + description: 'Use the host''s pid namespace. Optional: Default to false.' + type: boolean + hostUsers: + description: 'Use the host''s user namespace. Optional: Default to true. If set to true or not present, the pod will be run in the host user namespace, useful for when the pod needs a feature only available to the host user namespace, such as loading a kernel module with CAP_SYS_MODULE. When set to false, a new userns is created for the pod. Setting false is useful for mitigating container breakout vulnerabilities even allowing users to run their containers as root without actually having root privileges on the host. This field is alpha-level and is only honored by servers that enable the UserNamespacesSupport feature.' + type: boolean + hostname: + description: Specifies the hostname of the Pod If not specified, the pod's hostname will be set to a system-defined value. + type: string + imagePullSecrets: + description: 'ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod' + type: array + items: + description: LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace. + type: object + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + initContainers: + description: 'List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/' + type: array + items: + description: A single application container that you want to run within a pod. + type: object + required: + - name + properties: + args: + description: 'Arguments to the entrypoint. The container image''s CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container''s environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + type: array + items: + type: string + command: + description: 'Entrypoint array. Not executed within a shell. The container image''s ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container''s environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + type: array + items: + type: string + env: + description: List of environment variables to set in the container. Cannot be updated. + type: array + items: + description: EnvVar represents an environment variable present in a Container. + type: object + required: + - name + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's value. Cannot be used if value is not empty. + type: object + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + type: object + required: + - key + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap or its key must be defined + type: boolean + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs.' + type: object + required: + - fieldPath + properties: + apiVersion: + description: Version of the schema the FieldPath is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified API version. + type: string + resourceFieldRef: + description: 'Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported.' + type: object + required: + - resource + properties: + containerName: + description: 'Container name: required for volumes, optional for env vars' + type: string + divisor: + description: Specifies the output format of the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + type: object + required: + - key + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret or its key must be defined + type: boolean + envFrom: + description: List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated. + type: array + items: + description: EnvFromSource represents the source of a set of ConfigMaps + type: object + properties: + configMapRef: + description: The ConfigMap to select from + type: object + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap must be defined + type: boolean + prefix: + description: An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + type: object + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + image: + description: 'Container image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.' + type: string + imagePullPolicy: + description: 'Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' + type: string + lifecycle: + description: Actions that the management system should take in response to container lifecycle events. Cannot be updated. + type: object + properties: + postStart: + description: 'PostStart is called immediately after a container is created. If the handler fails, the container is terminated and restarted according to its restart policy. Other management of the container blocks until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + type: object + properties: + exec: + description: Exec specifies the action to take. + type: object + properties: + command: + description: Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + httpGet: + description: HTTPGet specifies the http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + path: + description: Path to access on the HTTP server. + type: string + port: + description: Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. Defaults to HTTP. + type: string + tcpSocket: + description: Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept for the backward compatibility. There are no validation of this field and lifecycle hooks will fail in runtime when tcp handler is specified. + type: object + required: + - port + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + preStop: + description: 'PreStop is called immediately before a container is terminated due to an API request or management event such as liveness/startup probe failure, preemption, resource contention, etc. The handler is not called if the container crashes or exits. The Pod''s termination grace period countdown begins before the PreStop hook is executed. Regardless of the outcome of the handler, the container will eventually terminate within the Pod''s termination grace period (unless delayed by finalizers). Other management of the container blocks until the hook completes or until the termination grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + type: object + properties: + exec: + description: Exec specifies the action to take. + type: object + properties: + command: + description: Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + httpGet: + description: HTTPGet specifies the http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + path: + description: Path to access on the HTTP server. + type: string + port: + description: Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. Defaults to HTTP. + type: string + tcpSocket: + description: Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept for the backward compatibility. There are no validation of this field and lifecycle hooks will fail in runtime when tcp handler is specified. + type: object + required: + - port + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + livenessProbe: + description: 'Periodic probe of container liveness. Container will be restarted if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: object + properties: + exec: + description: Exec specifies the action to take. + type: object + properties: + command: + description: Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + failureThreshold: + description: Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1. + type: integer + format: int32 + grpc: + description: GRPC specifies an action involving a GRPC port. + type: object + required: + - port + properties: + port: + description: Port number of the gRPC service. Number must be in the range 1 to 65535. + type: integer + format: int32 + service: + description: "Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). \n If this is not specified, the default behavior is defined by gRPC." + type: string + httpGet: + description: HTTPGet specifies the http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + path: + description: Path to access on the HTTP server. + type: string + port: + description: Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. Defaults to HTTP. + type: string + initialDelaySeconds: + description: 'Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + periodSeconds: + description: How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1. + type: integer + format: int32 + successThreshold: + description: Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + type: integer + format: int32 + tcpSocket: + description: TCPSocket specifies an action involving a TCP port. + type: object + required: + - port + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + type: integer + format: int64 + timeoutSeconds: + description: 'Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + name: + description: Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated. + type: string + ports: + description: List of ports to expose from the container. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default "0.0.0.0" address inside a container will be accessible from the network. Modifying this array with strategic merge patch may corrupt the data. For more information See https://github.com/kubernetes/kubernetes/issues/108255. Cannot be updated. + type: array + items: + description: ContainerPort represents a network port in a single container. + type: object + required: + - containerPort + properties: + containerPort: + description: Number of port to expose on the pod's IP address. This must be a valid port number, 0 < x < 65536. + type: integer + format: int32 + hostIP: + description: What host IP to bind the external port to. + type: string + hostPort: + description: Number of port to expose on the host. If specified, this must be a valid port number, 0 < x < 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this. + type: integer + format: int32 + name: + description: If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services. + type: string + protocol: + description: Protocol for port. Must be UDP, TCP, or SCTP. Defaults to "TCP". + type: string + default: TCP + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: 'Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: object + properties: + exec: + description: Exec specifies the action to take. + type: object + properties: + command: + description: Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + failureThreshold: + description: Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1. + type: integer + format: int32 + grpc: + description: GRPC specifies an action involving a GRPC port. + type: object + required: + - port + properties: + port: + description: Port number of the gRPC service. Number must be in the range 1 to 65535. + type: integer + format: int32 + service: + description: "Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). \n If this is not specified, the default behavior is defined by gRPC." + type: string + httpGet: + description: HTTPGet specifies the http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + path: + description: Path to access on the HTTP server. + type: string + port: + description: Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. Defaults to HTTP. + type: string + initialDelaySeconds: + description: 'Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + periodSeconds: + description: How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1. + type: integer + format: int32 + successThreshold: + description: Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + type: integer + format: int32 + tcpSocket: + description: TCPSocket specifies an action involving a TCP port. + type: object + required: + - port + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + type: integer + format: int64 + timeoutSeconds: + description: 'Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + resizePolicy: + description: Resources resize policy for the container. + type: array + items: + description: ContainerResizePolicy represents resource resize policy for the container. + type: object + required: + - resourceName + - restartPolicy + properties: + resourceName: + description: 'Name of the resource to which this resource resize policy applies. Supported values: cpu, memory.' + type: string + restartPolicy: + description: Restart policy to apply when specified resource is resized. If not specified, it defaults to NotRequired. + type: string + x-kubernetes-list-type: atomic + resources: + description: 'Compute Resources required by this container. Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + properties: + claims: + description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." + type: array + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + type: object + required: + - name + properties: + name: + description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. + type: string + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + additionalProperties: + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + requests: + description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + additionalProperties: + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + restartPolicy: + description: 'RestartPolicy defines the restart behavior of individual containers in a pod. This field may only be set for init containers, and the only allowed value is "Always". For non-init containers or when this field is not specified, the restart behavior is defined by the Pod''s restart policy and the container type. Setting the RestartPolicy as "Always" for the init container will have the following effect: this init container will be continually restarted on exit until all regular containers have terminated. Once all regular containers have completed, all init containers with restartPolicy "Always" will be shut down. This lifecycle differs from normal init containers and is often referred to as a "sidecar" container. Although this init container still starts in the init container sequence, it does not wait for the container to complete before proceeding to the next init container. Instead, the next init container starts immediately after this init container is started, or after any startupProbe has successfully completed.' + type: string + securityContext: + description: 'SecurityContext defines the security options the container should be run with. If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' + type: object + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN Note that this field cannot be set when spec.os.name is windows.' + type: boolean + capabilities: + description: The capabilities to add/drop when running containers. Defaults to the default set of capabilities granted by the container runtime. Note that this field cannot be set when spec.os.name is windows. + type: object + properties: + add: + description: Added capabilities + type: array + items: + description: Capability represent POSIX capabilities type + type: string + drop: + description: Removed capabilities + type: array + items: + description: Capability represent POSIX capabilities type + type: string + privileged: + description: Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false. Note that this field cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled. Note that this field cannot be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only root filesystem. Default is false. Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows. + type: integer + format: int64 + runAsNonRoot: + description: Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows. + type: integer + format: int64 + seLinuxOptions: + description: The SELinux context to be applied to the container. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows. + type: object + properties: + level: + description: Level is SELinux level label that applies to the container. + type: string + role: + description: Role is a SELinux role label that applies to the container. + type: string + type: + description: Type is a SELinux type label that applies to the container. + type: string + user: + description: User is a SELinux user label that applies to the container. + type: string + seccompProfile: + description: The seccomp options to use by this container. If seccomp options are provided at both the pod & container level, the container options override the pod options. Note that this field cannot be set when spec.os.name is windows. + type: object + required: + - type + properties: + localhostProfile: + description: localhostProfile indicates a profile defined in a file on the node should be used. The profile must be preconfigured on the node to work. Must be a descending path, relative to the kubelet's configured seccomp profile location. Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: "type indicates which kind of seccomp profile will be applied. Valid options are: \n Localhost - a profile defined in a file on the node should be used. RuntimeDefault - the container runtime default profile should be used. Unconfined - no profile should be applied." + type: string + windowsOptions: + description: The Windows specific settings applied to all containers. If unspecified, the options from the PodSecurityContext will be used. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is linux. + type: object + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should be run as a 'Host Process' container. All of a Pod's containers must have the same effective HostProcess value (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + startupProbe: + description: 'StartupProbe indicates that the Pod has successfully initialized. If specified, no other probes are executed until this completes successfully. If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. This can be used to provide different probe parameters at the beginning of a Pod''s lifecycle, when it might take a long time to load data or warm a cache, than during steady-state operation. This cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: object + properties: + exec: + description: Exec specifies the action to take. + type: object + properties: + command: + description: Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + failureThreshold: + description: Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1. + type: integer + format: int32 + grpc: + description: GRPC specifies an action involving a GRPC port. + type: object + required: + - port + properties: + port: + description: Port number of the gRPC service. Number must be in the range 1 to 65535. + type: integer + format: int32 + service: + description: "Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). \n If this is not specified, the default behavior is defined by gRPC." + type: string + httpGet: + description: HTTPGet specifies the http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + path: + description: Path to access on the HTTP server. + type: string + port: + description: Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. Defaults to HTTP. + type: string + initialDelaySeconds: + description: 'Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + periodSeconds: + description: How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1. + type: integer + format: int32 + successThreshold: + description: Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + type: integer + format: int32 + tcpSocket: + description: TCPSocket specifies an action involving a TCP port. + type: object + required: + - port + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + type: integer + format: int64 + timeoutSeconds: + description: 'Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + stdin: + description: Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false. + type: boolean + stdinOnce: + description: Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false + type: boolean + terminationMessagePath: + description: 'Optional: Path at which the file to which the container''s termination message will be written is mounted into the container''s filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.' + type: string + terminationMessagePolicy: + description: Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated. + type: string + tty: + description: Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices to be used by the container. + type: array + items: + description: volumeDevice describes a mapping of a raw block device within a container. + type: object + required: + - devicePath + - name + properties: + devicePath: + description: devicePath is the path inside of the container that the device will be mapped to. + type: string + name: + description: name must match the name of a persistentVolumeClaim in the pod + type: string + volumeMounts: + description: Pod volumes to mount into the container's filesystem. Cannot be updated. + type: array + items: + description: VolumeMount describes a mounting of a Volume within a container. + type: object + required: + - mountPath + - name + properties: + mountPath: + description: Path within the container at which the volume should be mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false. + type: boolean + subPath: + description: Path within the volume from which the container's volume should be mounted. Defaults to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume from which the container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to "" (volume's root). SubPathExpr and SubPath are mutually exclusive. + type: string + workingDir: + description: Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated. + type: string + nodeName: + description: NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements. + type: string + nodeSelector: + description: 'NodeSelector is a selector which must be true for the pod to fit on a node. Selector which must match a node''s labels for the pod to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' + type: object + additionalProperties: + type: string + x-kubernetes-map-type: atomic + os: + description: "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set. \n If the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions \n If the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.hostUsers - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup" + type: object + required: + - name + properties: + name: + description: 'Name is the name of the operating system. The currently supported values are linux and windows. Additional value may be defined in future and can be one of: https://github.com/opencontainers/runtime-spec/blob/master/config.md#platform-specific-configuration Clients should expect to handle additional values and treat unrecognized values in this field as os: null' + type: string + overhead: + description: 'Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md' + type: object + additionalProperties: + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + preemptionPolicy: + description: PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. + type: string + priority: + description: The priority value. Various system components use this field to find the priority of the pod. When Priority Admission Controller is enabled, it prevents users from setting this field. The admission controller populates this field from PriorityClassName. The higher the value, the higher the priority. + type: integer + format: int32 + priorityClassName: + description: If specified, indicates the pod's priority. "system-node-critical" and "system-cluster-critical" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default. + type: string + readinessGates: + description: 'If specified, all readiness gates will be evaluated for pod readiness. A pod is ready when all its containers are ready AND all conditions specified in the readiness gates have status equal to "True" More info: https://git.k8s.io/enhancements/keps/sig-network/580-pod-readiness-gates' + type: array + items: + description: PodReadinessGate contains the reference to a pod condition + type: object + required: + - conditionType + properties: + conditionType: + description: ConditionType refers to a condition in the pod's condition list with matching type. + type: string + resourceClaims: + description: "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable." + type: array + items: + description: PodResourceClaim references exactly one ResourceClaim through a ClaimSource. It adds a name to it that uniquely identifies the ResourceClaim inside the Pod. Containers that need access to the ResourceClaim reference it with this name. + type: object + required: + - name + properties: + name: + description: Name uniquely identifies this resource claim inside the pod. This must be a DNS_LABEL. + type: string + source: + description: Source describes where to find the ResourceClaim. + type: object + properties: + resourceClaimName: + description: ResourceClaimName is the name of a ResourceClaim object in the same namespace as this pod. + type: string + resourceClaimTemplateName: + description: "ResourceClaimTemplateName is the name of a ResourceClaimTemplate object in the same namespace as this pod. \n The template will be used to create a new ResourceClaim, which will be bound to this pod. When this pod is deleted, the ResourceClaim will also be deleted. The pod name and resource name, along with a generated component, will be used to form a unique name for the ResourceClaim, which will be recorded in pod.status.resourceClaimStatuses. \n This field is immutable and no changes will be made to the corresponding ResourceClaim by the control plane after creating the ResourceClaim." + type: string + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + restartPolicy: + description: 'Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy' + type: string + runtimeClassName: + description: 'RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the "legacy" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class' + type: string + schedulerName: + description: If specified, the pod will be dispatched by specified scheduler. If not specified, the pod will be dispatched by default scheduler. + type: string + schedulingGates: + description: "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. \n SchedulingGates can only be set at pod creation time, and be removed only afterwards. \n This is a beta feature enabled by the PodSchedulingReadiness feature gate." + type: array + items: + description: PodSchedulingGate is associated to a Pod to guard its scheduling. + type: object + required: + - name + properties: + name: + description: Name of the scheduling gate. Each scheduling gate must have a unique name field. + type: string + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + securityContext: + description: 'SecurityContext holds pod-level security attributes and common container settings. Optional: Defaults to empty. See type description for default values of each field.' + type: object + properties: + fsGroup: + description: "A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to change the ownership of that volume to be owned by the pod: \n 1. The owning GID will be the FSGroup 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) 3. The permission bits are OR'd with rw-rw---- \n If unset, the Kubelet will not modify the ownership and permissions of any volume. Note that this field cannot be set when spec.os.name is windows." + type: integer + format: int64 + fsGroupChangePolicy: + description: 'fsGroupChangePolicy defines behavior of changing ownership and permission of the volume before being exposed inside Pod. This field will only apply to volume types which support fsGroup based ownership(and permissions). It will have no effect on ephemeral volume types such as: secret, configmaps and emptydir. Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. Note that this field cannot be set when spec.os.name is windows.' + type: string + runAsGroup: + description: The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container. Note that this field cannot be set when spec.os.name is windows. + type: integer + format: int64 + runAsNonRoot: + description: Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container. Note that this field cannot be set when spec.os.name is windows. + type: integer + format: int64 + seLinuxOptions: + description: The SELinux context to be applied to all containers. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container. Note that this field cannot be set when spec.os.name is windows. + type: object + properties: + level: + description: Level is SELinux level label that applies to the container. + type: string + role: + description: Role is a SELinux role label that applies to the container. + type: string + type: + description: Type is a SELinux type label that applies to the container. + type: string + user: + description: User is a SELinux user label that applies to the container. + type: string + seccompProfile: + description: The seccomp options to use by the containers in this pod. Note that this field cannot be set when spec.os.name is windows. + type: object + required: + - type + properties: + localhostProfile: + description: localhostProfile indicates a profile defined in a file on the node should be used. The profile must be preconfigured on the node to work. Must be a descending path, relative to the kubelet's configured seccomp profile location. Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: "type indicates which kind of seccomp profile will be applied. Valid options are: \n Localhost - a profile defined in a file on the node should be used. RuntimeDefault - the container runtime default profile should be used. Unconfined - no profile should be applied." + type: string + supplementalGroups: + description: A list of groups applied to the first process run in each container, in addition to the container's primary GID, the fsGroup (if specified), and group memberships defined in the container image for the uid of the container process. If unspecified, no additional groups are added to any container. Note that group memberships defined in the container image for the uid of the container process are still effective, even if they are not included in this list. Note that this field cannot be set when spec.os.name is windows. + type: array + items: + type: integer + format: int64 + sysctls: + description: Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported sysctls (by the container runtime) might fail to launch. Note that this field cannot be set when spec.os.name is windows. + type: array + items: + description: Sysctl defines a kernel parameter to be set + type: object + required: + - name + - value + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + windowsOptions: + description: The Windows specific settings applied to all containers. If unspecified, the options within a container's SecurityContext will be used. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is linux. + type: object + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should be run as a 'Host Process' container. All of a Pod's containers must have the same effective HostProcess value (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + serviceAccount: + description: 'DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.' + type: string + serviceAccountName: + description: 'ServiceAccountName is the name of the ServiceAccount to use to run this pod. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/' + type: string + setHostnameAsFQDN: + description: If true the pod's hostname will be configured as the pod's FQDN, rather than the leaf name (the default). In Linux containers, this means setting the FQDN in the hostname field of the kernel (the nodename field of struct utsname). In Windows containers, this means setting the registry value of hostname for the registry key HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters to FQDN. If a pod does not have FQDN, this has no effect. Default to false. + type: boolean + shareProcessNamespace: + description: 'Share a single process namespace between all of the containers in a pod. When this is set containers will be able to view and signal processes from other containers in the same pod, and the first process in each container will not be assigned PID 1. HostPID and ShareProcessNamespace cannot both be set. Optional: Default to false.' + type: boolean + subdomain: + description: If specified, the fully qualified Pod hostname will be "...svc.". If not specified, the pod will not have a domainname at all. + type: string + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs to terminate gracefully. May be decreased in delete request. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). If this value is nil, the default grace period will be used instead. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. Defaults to 30 seconds. + type: integer + format: int64 + tolerations: + description: If specified, the pod's tolerations. + type: array + items: + description: The pod this Toleration is attached to tolerates any taint that matches the triple using the matching operator . + type: object + 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. + type: integer + format: int64 + 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 + topologySpreadConstraints: + description: TopologySpreadConstraints describes how a group of pods ought to spread across topology domains. Scheduler will schedule pods in a way which abides by the constraints. All topologySpreadConstraints are ANDed. + type: array + items: + description: TopologySpreadConstraint specifies how to spread matching pods among the given topology. + type: object + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + properties: + labelSelector: + description: LabelSelector is used to find matching pods. Pods that match this label selector are counted to determine the number of pods in their corresponding topology domain. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + matchLabelKeys: + description: "MatchLabelKeys is a set of pod label keys to select the pods over which spreading will be calculated. The keys are used to lookup values from the incoming pod labels, those key-value labels are ANDed with labelSelector to select the group of existing pods over which spreading will be calculated for the incoming pod. The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. MatchLabelKeys cannot be set when LabelSelector isn't set. Keys that don't exist in the incoming pod labels will be ignored. A null or empty list means only match against labelSelector. \n This is a beta field and requires the MatchLabelKeysInPodTopologySpread feature gate to be enabled (enabled by default)." + type: array + items: + type: string + x-kubernetes-list-type: atomic + maxSkew: + description: 'MaxSkew describes the degree to which pods may be unevenly distributed. When `whenUnsatisfiable=DoNotSchedule`, it is the maximum permitted difference between the number of matching pods in the target topology and the global minimum. The global minimum is the minimum number of matching pods in an eligible domain or zero if the number of eligible domains is less than MinDomains. For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 2/2/1: In this case, the global minimum is 1. | zone1 | zone2 | zone3 | | P P | P P | P | - if MaxSkew is 1, incoming pod can only be scheduled to zone3 to become 2/2/2; scheduling it onto zone1(zone2) would make the ActualSkew(3-1) on zone1(zone2) violate MaxSkew(1). - if MaxSkew is 2, incoming pod can be scheduled onto any zone. When `whenUnsatisfiable=ScheduleAnyway`, it is used to give higher precedence to topologies that satisfy it. It''s a required field. Default value is 1 and 0 is not allowed.' + type: integer + format: int32 + minDomains: + description: "MinDomains indicates a minimum number of eligible domains. When the number of eligible domains with matching topology keys is less than minDomains, Pod Topology Spread treats \"global minimum\" as 0, and then the calculation of Skew is performed. And when the number of eligible domains with matching topology keys equals or greater than minDomains, this value has no effect on scheduling. As a result, when the number of eligible domains is less than minDomains, scheduler won't schedule more than maxSkew Pods to those domains. If value is nil, the constraint behaves as if MinDomains is equal to 1. Valid values are integers greater than 0. When value is not nil, WhenUnsatisfiable must be DoNotSchedule. \n For example, in a 3-zone cluster, MaxSkew is set to 2, MinDomains is set to 5 and pods with the same labelSelector spread as 2/2/2: | zone1 | zone2 | zone3 | | P P | P P | P P | The number of domains is less than 5(MinDomains), so \"global minimum\" is treated as 0. In this situation, new pod with the same labelSelector cannot be scheduled, because computed skew will be 3(3 - 0) if new Pod is scheduled to any of the three zones, it will violate MaxSkew. \n This is a beta field and requires the MinDomainsInPodTopologySpread feature gate to be enabled (enabled by default)." + type: integer + format: int32 + nodeAffinityPolicy: + description: "NodeAffinityPolicy indicates how we will treat Pod's nodeAffinity/nodeSelector when calculating pod topology spread skew. Options are: - Honor: only nodes matching nodeAffinity/nodeSelector are included in the calculations. - Ignore: nodeAffinity/nodeSelector are ignored. All nodes are included in the calculations. \n If this value is nil, the behavior is equivalent to the Honor policy. This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag." + type: string + nodeTaintsPolicy: + description: "NodeTaintsPolicy indicates how we will treat node taints when calculating pod topology spread skew. Options are: - Honor: nodes without taints, along with tainted nodes for which the incoming pod has a toleration, are included. - Ignore: node taints are ignored. All nodes are included. \n If this value is nil, the behavior is equivalent to the Ignore policy. This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag." + type: string + topologyKey: + description: TopologyKey is the key of node labels. Nodes that have a label with this key and identical values are considered to be in the same topology. We consider each as a "bucket", and try to put balanced number of pods into each bucket. We define a domain as a particular instance of a topology. Also, we define an eligible domain as a domain whose nodes meet the requirements of nodeAffinityPolicy and nodeTaintsPolicy. e.g. If TopologyKey is "kubernetes.io/hostname", each Node is a domain of that topology. And, if TopologyKey is "topology.kubernetes.io/zone", each zone is a domain of that topology. It's a required field. + type: string + whenUnsatisfiable: + description: 'WhenUnsatisfiable indicates how to deal with a pod if it doesn''t satisfy the spread constraint. - DoNotSchedule (default) tells the scheduler not to schedule it. - ScheduleAnyway tells the scheduler to schedule the pod in any location, but giving higher precedence to topologies that would help reduce the skew. A constraint is considered "Unsatisfiable" for an incoming pod if and only if every possible node assignment for that pod would violate "MaxSkew" on some topology. For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 3/1/1: | zone1 | zone2 | zone3 | | P P P | P | P | If WhenUnsatisfiable is set to DoNotSchedule, incoming pod can only be scheduled to zone2(zone3) to become 3/2/1(3/1/2) as ActualSkew(2-1) on zone2(zone3) satisfies MaxSkew(1). In other words, the cluster can still be imbalanced, but scheduler won''t make it *more* imbalanced. It''s a required field.' + type: string + x-kubernetes-list-map-keys: + - topologyKey + - whenUnsatisfiable + x-kubernetes-list-type: map + volumes: + description: 'List of volumes that can be mounted by containers belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes' + type: array + items: + description: Volume represents a named volume in a pod that may be accessed by any container in the pod. + type: object + required: + - name + properties: + awsElasticBlockStore: + description: 'awsElasticBlockStore represents an AWS Disk resource that is attached to a kubelet''s host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: object + required: + - volumeID + properties: + fsType: + description: 'fsType is the filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore TODO: how do we prevent errors in the filesystem from compromising the machine' + type: string + partition: + description: 'partition is the partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as "1". Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty).' + type: integer + format: int32 + readOnly: + description: 'readOnly value true will force the readOnly setting in VolumeMounts. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'volumeID is unique ID of the persistent disk resource in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + azureDisk: + description: azureDisk represents an Azure Data Disk mount on the host and bind mount to the pod. + type: object + required: + - diskName + - diskURI + properties: + cachingMode: + description: 'cachingMode is the Host Caching mode: None, Read Only, Read Write.' + type: string + diskName: + description: diskName is the Name of the data disk in the blob storage + type: string + diskURI: + description: diskURI is the URI of data disk in the blob storage + type: string + fsType: + description: fsType is Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + kind: + description: 'kind expected values are Shared: multiple blob disks per storage account Dedicated: single blob disk per storage account Managed: azure managed data disk (only in managed availability set). defaults to shared' + type: string + readOnly: + description: readOnly Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. + type: boolean + azureFile: + description: azureFile represents an Azure File Service mount on the host and bind mount to the pod. + type: object + required: + - secretName + - shareName + properties: + readOnly: + description: readOnly defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: secretName is the name of secret that contains Azure Storage Account Name and Key + type: string + shareName: + description: shareName is the azure share Name + type: string + cephfs: + description: cephFS represents a Ceph FS mount on the host that shares a pod's lifetime + type: object + required: + - monitors + properties: + monitors: + description: 'monitors is Required: Monitors is a collection of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: array + items: + type: string + path: + description: 'path is Optional: Used as the mounted root, rather than the full Ceph tree, default is /' + type: string + readOnly: + description: 'readOnly is Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'secretFile is Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'secretRef is Optional: SecretRef is reference to the authentication secret for User, default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: object + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + user: + description: 'user is optional: User is the rados user name, default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + cinder: + description: 'cinder represents a cinder volume attached and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: object + required: + - volumeID + properties: + fsType: + description: 'fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'readOnly defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'secretRef is optional: points to a secret object containing parameters used to connect to OpenStack.' + type: object + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + volumeID: + description: 'volumeID used to identify the volume in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + configMap: + description: configMap represents a configMap that should populate this volume + type: object + properties: + defaultMode: + description: 'defaultMode is optional: mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.' + type: integer + format: int32 + items: + description: items if unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the ConfigMap, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.' + type: integer + format: int32 + path: + description: path is the relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: optional specify whether the ConfigMap or its keys must be defined + type: boolean + csi: + description: csi (Container Storage Interface) represents ephemeral storage that is handled by certain external CSI drivers (Beta feature). + type: object + required: + - driver + properties: + driver: + description: driver is the name of the CSI driver that handles this volume. Consult with your admin for the correct name as registered in the cluster. + type: string + fsType: + description: fsType to mount. Ex. "ext4", "xfs", "ntfs". If not provided, the empty value is passed to the associated CSI driver which will determine the default filesystem to apply. + type: string + nodePublishSecretRef: + description: nodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secret references are passed. + type: object + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + readOnly: + description: readOnly specifies a read-only configuration for the volume. Defaults to false (read/write). + type: boolean + volumeAttributes: + description: volumeAttributes stores driver-specific properties that are passed to the CSI driver. Consult your driver's documentation for supported values. + type: object + additionalProperties: + type: string + downwardAPI: + description: downwardAPI represents downward API about the pod that should populate this volume + type: object + properties: + defaultMode: + description: 'Optional: mode bits to use on created files by default. Must be a Optional: mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.' + type: integer + format: int32 + items: + description: Items is a list of downward API volume file + type: array + items: + description: DownwardAPIVolumeFile represents information to create the file containing the pod field + type: object + required: + - path + properties: + fieldRef: + description: 'Required: Selects a field of the pod: only annotations, labels, name and namespace are supported.' + type: object + required: + - fieldPath + properties: + apiVersion: + description: Version of the schema the FieldPath is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified API version. + type: string + mode: + description: 'Optional: mode bits used to set permissions on this file, must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.' + type: integer + format: int32 + path: + description: 'Required: Path is the relative path name of the file to be created. Must not be absolute or contain the ''..'' path. Must be utf-8 encoded. The first item of the relative path must not start with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported.' + type: object + required: + - resource + properties: + containerName: + description: 'Container name: required for volumes, optional for env vars' + type: string + divisor: + description: Specifies the output format of the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + emptyDir: + description: 'emptyDir represents a temporary directory that shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: object + properties: + medium: + description: 'medium represents what type of storage medium should back this directory. The default is "" which means to use the node''s default medium. Must be an empty string (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: string + sizeLimit: + description: 'sizeLimit is the total amount of local storage required for this EmptyDir volume. The size limit is also applicable for memory medium. The maximum usage on memory medium EmptyDir would be the minimum value between the SizeLimit specified here and the sum of memory limits of all containers in a pod. The default is nil which means that the limit is undefined. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + ephemeral: + description: "ephemeral represents a volume that is handled by a cluster storage driver. The volume's lifecycle is tied to the pod that defines it - it will be created before the pod starts, and deleted when the pod is removed. \n Use this if: a) the volume is only needed while the pod runs, b) features of normal volumes like restoring from snapshot or capacity tracking are needed, c) the storage driver is specified through a storage class, and d) the storage driver supports dynamic volume provisioning through a PersistentVolumeClaim (see EphemeralVolumeSource for more information on the connection between this volume type and PersistentVolumeClaim). \n Use PersistentVolumeClaim or one of the vendor-specific APIs for volumes that persist for longer than the lifecycle of an individual pod. \n Use CSI for light-weight local ephemeral volumes if the CSI driver is meant to be used that way - see the documentation of the driver for more information. \n A pod can use both types of ephemeral volumes and persistent volumes at the same time." + type: object + properties: + volumeClaimTemplate: + description: "Will be used to create a stand-alone PVC to provision the volume. The pod in which this EphemeralVolumeSource is embedded will be the owner of the PVC, i.e. the PVC will be deleted together with the pod. The name of the PVC will be `-` where `` is the name from the `PodSpec.Volumes` array entry. Pod validation will reject the pod if the concatenated name is not valid for a PVC (for example, too long). \n An existing PVC with that name that is not owned by the pod will *not* be used for the pod to avoid using an unrelated volume by mistake. Starting the pod is then blocked until the unrelated PVC is removed. If such a pre-created PVC is meant to be used by the pod, the PVC has to updated with an owner reference to the pod once the pod exists. Normally this should not be necessary, but it may be useful when manually reconstructing a broken cluster. \n This field is read-only and no changes will be made by Kubernetes to the PVC after it has been created. \n Required, must not be nil." + type: object + required: + - spec + properties: + metadata: + description: May contain labels and annotations that will be copied into the PVC when creating it. No other fields are allowed and will be rejected during validation. + type: object + spec: + description: The specification for the PersistentVolumeClaim. The entire content is copied unchanged into the PVC that gets created from this template. The same fields as in a PersistentVolumeClaim are also valid here. + type: object + properties: + accessModes: + description: 'accessModes contains the desired access modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + type: array + items: + type: string + dataSource: + description: 'dataSource field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) * An existing PVC (PersistentVolumeClaim) If the provisioner or an external controller can support the specified data source, it will create a new volume based on the contents of the specified data source. When the AnyVolumeDataSource feature gate is enabled, dataSource contents will be copied to dataSourceRef, and dataSourceRef contents will be copied to dataSource when dataSourceRef.namespace is not specified. If the namespace is specified, then dataSourceRef will not be copied to dataSource.' + type: object + required: + - kind + - name + properties: + apiGroup: + description: APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being referenced + type: string + name: + description: Name is the name of resource being referenced + type: string + dataSourceRef: + description: 'dataSourceRef specifies the object from which to populate the volume with data, if a non-empty volume is desired. This may be any object from a non-empty API group (non core object) or a PersistentVolumeClaim object. When this field is specified, volume binding will only succeed if the type of the specified object matches some installed volume populator or dynamic provisioner. This field will replace the functionality of the dataSource field and as such if both fields are non-empty, they must have the same value. For backwards compatibility, when namespace isn''t specified in dataSourceRef, both fields (dataSource and dataSourceRef) will be set to the same value automatically if one of them is empty and the other is non-empty. When namespace is specified in dataSourceRef, dataSource isn''t set to the same value and must be empty. There are three important differences between dataSource and dataSourceRef: * While dataSource only allows two specific types of objects, dataSourceRef allows any non-core object, as well as PersistentVolumeClaim objects. * While dataSource ignores disallowed values (dropping them), dataSourceRef preserves all values, and generates an error if a disallowed value is specified. * While dataSource only allows local objects, dataSourceRef allows objects in any namespaces. (Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled. (Alpha) Using the namespace field of dataSourceRef requires the CrossNamespaceVolumeDataSource feature gate to be enabled.' + type: object + required: + - kind + - name + properties: + apiGroup: + description: APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being referenced + type: string + name: + description: Name is the name of resource being referenced + type: string + namespace: + description: Namespace is the namespace of resource being referenced Note that when a namespace is specified, a gateway.networking.k8s.io/ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. (Alpha) This field requires the CrossNamespaceVolumeDataSource feature gate to be enabled. + type: string + resources: + description: 'resources represents the minimum resources the volume should have. If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements that are lower than previous value but must still be higher than capacity recorded in the status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + type: object + properties: + claims: + description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." + type: array + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + type: object + required: + - name + properties: + name: + description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. + type: string + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + additionalProperties: + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + requests: + description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + additionalProperties: + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + selector: + description: selector is a label query over volumes to consider for binding. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + storageClassName: + description: 'storageClassName is the name of the StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + type: string + volumeMode: + description: volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. + type: string + volumeName: + description: volumeName is the binding reference to the PersistentVolume backing this claim. + type: string + fc: + description: fc represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod. + type: object + properties: + fsType: + description: 'fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. TODO: how do we prevent errors in the filesystem from compromising the machine' + type: string + lun: + description: 'lun is Optional: FC target lun number' + type: integer + format: int32 + readOnly: + description: 'readOnly is Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.' + type: boolean + targetWWNs: + description: 'targetWWNs is Optional: FC target worldwide names (WWNs)' + type: array + items: + type: string + wwids: + description: 'wwids Optional: FC volume world wide identifiers (wwids) Either wwids or combination of targetWWNs and lun must be set, but not both simultaneously.' + type: array + items: + type: string + flexVolume: + description: flexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin. + type: object + required: + - driver + properties: + driver: + description: driver is the name of the driver to use for this volume. + type: string + fsType: + description: fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". The default filesystem depends on FlexVolume script. + type: string + options: + description: 'options is Optional: this field holds extra command options if any.' + type: object + additionalProperties: + type: string + readOnly: + description: 'readOnly is Optional: defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.' + type: boolean + secretRef: + description: 'secretRef is Optional: secretRef is reference to the secret object containing sensitive information to pass to the plugin scripts. This may be empty if no secret object is specified. If the secret object contains more than one secret, all secrets are passed to the plugin scripts.' + type: object + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + flocker: + description: flocker represents a Flocker volume attached to a kubelet's host machine. This depends on the Flocker control service being running + type: object + properties: + datasetName: + description: datasetName is Name of the dataset stored as metadata -> name on the dataset for Flocker should be considered as deprecated + type: string + datasetUUID: + description: datasetUUID is the UUID of the dataset. This is unique identifier of a Flocker dataset + type: string + gcePersistentDisk: + description: 'gcePersistentDisk represents a GCE Disk resource that is attached to a kubelet''s host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: object + required: + - pdName + properties: + fsType: + description: 'fsType is filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk TODO: how do we prevent errors in the filesystem from compromising the machine' + type: string + partition: + description: 'partition is the partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as "1". Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: integer + format: int32 + pdName: + description: 'pdName is unique name of the PD resource in GCE. Used to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'readOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + gitRepo: + description: 'gitRepo represents a git repository at a particular revision. DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir into the Pod''s container.' + type: object + required: + - repository + properties: + directory: + description: directory is the target directory name. Must not contain or start with '..'. If '.' is supplied, the volume directory will be the git repository. Otherwise, if specified, the volume will contain the git repository in the subdirectory with the given name. + type: string + repository: + description: repository is the URL + type: string + revision: + description: revision is the commit hash for the specified revision. + type: string + glusterfs: + description: 'glusterfs represents a Glusterfs mount on the host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + type: object + required: + - endpoints + - path + properties: + endpoints: + description: 'endpoints is the endpoint name that details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'path is the Glusterfs volume path. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'readOnly here will force the Glusterfs volume to be mounted with read-only permissions. Defaults to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + hostPath: + description: 'hostPath represents a pre-existing file or directory on the host machine that is directly exposed to the container. This is generally used for system agents or other privileged things that are allowed to see the host machine. Most containers will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath --- TODO(jonesdl) We need to restrict who can use host directory mounts and who can/can not mount host directories as read/write.' + type: object + required: + - path + properties: + path: + description: 'path of the directory on the host. If the path is a symlink, it will follow the link to the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'type for HostPath Volume Defaults to "" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + iscsi: + description: 'iscsi represents an ISCSI Disk resource that is attached to a kubelet''s host machine and then exposed to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + type: object + required: + - iqn + - lun + - targetPortal + properties: + chapAuthDiscovery: + description: chapAuthDiscovery defines whether support iSCSI Discovery CHAP authentication + type: boolean + chapAuthSession: + description: chapAuthSession defines whether support iSCSI Session CHAP authentication + type: boolean + fsType: + description: 'fsType is the filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi TODO: how do we prevent errors in the filesystem from compromising the machine' + type: string + initiatorName: + description: initiatorName is the custom iSCSI Initiator Name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface : will be created for the connection. + type: string + iqn: + description: iqn is the target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iscsiInterface is the interface Name that uses an iSCSI transport. Defaults to 'default' (tcp). + type: string + lun: + description: lun represents iSCSI Target Lun number. + type: integer + format: int32 + portals: + description: portals is the iSCSI Target Portal List. The portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260). + type: array + items: + type: string + readOnly: + description: readOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: secretRef is the CHAP Secret for iSCSI target and initiator authentication + type: object + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + targetPortal: + description: targetPortal is iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260). + type: string + name: + description: 'name of the volume. Must be a DNS_LABEL and unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'nfs represents an NFS mount on the host that shares a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: object + required: + - path + - server + properties: + path: + description: 'path that is exported by the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'readOnly here will force the NFS export to be mounted with read-only permissions. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'server is the hostname or IP address of the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + persistentVolumeClaim: + description: 'persistentVolumeClaimVolumeSource represents a reference to a PersistentVolumeClaim in the same namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: object + required: + - claimName + properties: + claimName: + description: 'claimName is the name of a PersistentVolumeClaim in the same namespace as the pod using this volume. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: readOnly Will force the ReadOnly setting in VolumeMounts. Default false. + type: boolean + photonPersistentDisk: + description: photonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine + type: object + required: + - pdID + properties: + fsType: + description: fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + pdID: + description: pdID is the ID that identifies Photon Controller persistent disk + type: string + portworxVolume: + description: portworxVolume represents a portworx volume attached and mounted on kubelets host machine + type: object + required: + - volumeID + properties: + fsType: + description: fSType represents the filesystem type to mount Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs". Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: readOnly defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: volumeID uniquely identifies a Portworx volume + type: string + projected: + description: projected items for all in one resources secrets, configmaps, and downward API + type: object + properties: + defaultMode: + description: defaultMode are the mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + sources: + description: sources is the list of volume projections + type: array + items: + description: Projection that may be projected along with other supported volume types + type: object + properties: + configMap: + description: configMap information about the configMap data to project + type: object + properties: + items: + description: items if unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the ConfigMap, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.' + type: integer + format: int32 + path: + description: path is the relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: optional specify whether the ConfigMap or its keys must be defined + type: boolean + downwardAPI: + description: downwardAPI information about the downwardAPI data to project + type: object + properties: + items: + description: Items is a list of DownwardAPIVolume file + type: array + items: + description: DownwardAPIVolumeFile represents information to create the file containing the pod field + type: object + required: + - path + properties: + fieldRef: + description: 'Required: Selects a field of the pod: only annotations, labels, name and namespace are supported.' + type: object + required: + - fieldPath + properties: + apiVersion: + description: Version of the schema the FieldPath is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified API version. + type: string + mode: + description: 'Optional: mode bits used to set permissions on this file, must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.' + type: integer + format: int32 + path: + description: 'Required: Path is the relative path name of the file to be created. Must not be absolute or contain the ''..'' path. Must be utf-8 encoded. The first item of the relative path must not start with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported.' + type: object + required: + - resource + properties: + containerName: + description: 'Container name: required for volumes, optional for env vars' + type: string + divisor: + description: Specifies the output format of the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + secret: + description: secret information about the secret data to project + type: object + properties: + items: + description: items if unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the Secret, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.' + type: integer + format: int32 + path: + description: path is the relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: optional field specify whether the Secret or its key must be defined + type: boolean + serviceAccountToken: + description: serviceAccountToken is information about the serviceAccountToken data to project + type: object + required: + - path + properties: + audience: + description: audience is the intended audience of the token. A recipient of a token must identify itself with an identifier specified in the audience of the token, and otherwise should reject the token. The audience defaults to the identifier of the apiserver. + type: string + expirationSeconds: + description: expirationSeconds is the requested duration of validity of the service account token. As the token approaches expiration, the kubelet volume plugin will proactively rotate the service account token. The kubelet will start trying to rotate the token if the token is older than 80 percent of its time to live or if the token is older than 24 hours.Defaults to 1 hour and must be at least 10 minutes. + type: integer + format: int64 + path: + description: path is the path relative to the mount point of the file to project the token into. + type: string + quobyte: + description: quobyte represents a Quobyte mount on the host that shares a pod's lifetime + type: object + required: + - registry + - volume + properties: + group: + description: group to map volume access to Default is no group + type: string + readOnly: + description: readOnly here will force the Quobyte volume to be mounted with read-only permissions. Defaults to false. + type: boolean + registry: + description: registry represents a single or multiple Quobyte Registry services specified as a string as host:port pair (multiple entries are separated with commas) which acts as the central registry for volumes + type: string + tenant: + description: tenant owning the given Quobyte volume in the Backend Used with dynamically provisioned Quobyte volumes, value is set by the plugin + type: string + user: + description: user to map volume access to Defaults to serivceaccount user + type: string + volume: + description: volume is a string that references an already created Quobyte volume by name. + type: string + rbd: + description: 'rbd represents a Rados Block Device mount on the host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md' + type: object + required: + - image + - monitors + properties: + fsType: + description: 'fsType is the filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd TODO: how do we prevent errors in the filesystem from compromising the machine' + type: string + image: + description: 'image is the rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'keyring is the path to key ring for RBDUser. Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'monitors is a collection of Ceph monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: array + items: + type: string + pool: + description: 'pool is the rados pool name. Default is rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'readOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'secretRef is name of the authentication secret for RBDUser. If provided overrides keyring. Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: object + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + user: + description: 'user is the rados user name. Default is admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + scaleIO: + description: scaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes. + type: object + required: + - gateway + - secretRef + - system + properties: + fsType: + description: fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Default is "xfs". + type: string + gateway: + description: gateway is the host address of the ScaleIO API Gateway. + type: string + protectionDomain: + description: protectionDomain is the name of the ScaleIO Protection Domain for the configured storage. + type: string + readOnly: + description: readOnly Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: secretRef references to the secret for ScaleIO user and other sensitive information. If this is not provided, Login operation will fail. + type: object + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + sslEnabled: + description: sslEnabled Flag enable/disable SSL communication with Gateway, default false + type: boolean + storageMode: + description: storageMode indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned. + type: string + storagePool: + description: storagePool is the ScaleIO Storage Pool associated with the protection domain. + type: string + system: + description: system is the name of the storage system as configured in ScaleIO. + type: string + volumeName: + description: volumeName is the name of a volume already created in the ScaleIO system that is associated with this volume source. + type: string + secret: + description: 'secret represents a secret that should populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: object + properties: + defaultMode: + description: 'defaultMode is Optional: mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.' + type: integer + format: int32 + items: + description: items If unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the Secret, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.' + type: integer + format: int32 + path: + description: path is the relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'. + type: string + optional: + description: optional field specify whether the Secret or its keys must be defined + type: boolean + secretName: + description: 'secretName is the name of the secret in the pod''s namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + storageos: + description: storageOS represents a StorageOS volume attached and mounted on Kubernetes nodes. + type: object + properties: + fsType: + description: fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: readOnly defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: secretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted. + type: object + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + volumeName: + description: volumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace. + type: string + volumeNamespace: + description: volumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to "default" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created. + type: string + vsphereVolume: + description: vsphereVolume represents a vSphere volume attached and mounted on kubelets host machine + type: object + required: + - volumePath + properties: + fsType: + description: fsType is filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + storagePolicyID: + description: storagePolicyID is the storage Policy Based Management (SPBM) profile ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: storagePolicyName is the storage Policy Based Management (SPBM) profile name. + type: string + volumePath: + description: volumePath is the path that identifies vSphere volume vmdk + type: string + permissions: + type: array + items: + description: StrategyDeploymentPermissions describe the rbac rules and service account needed by the install strategy + type: object + required: + - rules + - serviceAccountName + properties: + rules: + type: array + items: + description: PolicyRule holds information that describes a policy rule, but does not contain information about who the rule applies to or which namespace the rule applies to. + type: object + required: + - verbs + properties: + apiGroups: + description: APIGroups is the name of the APIGroup that contains the resources. If multiple API groups are specified, any action requested against one of the enumerated resources in any API group will be allowed. "" represents the core API group and "*" represents all API groups. + type: array + items: + type: string + nonResourceURLs: + description: NonResourceURLs is a set of partial urls that a user should have access to. *s are allowed, but only as the full, final step in the path Since non-resource URLs are not namespaced, this field is only applicable for ClusterRoles referenced from a ClusterRoleBinding. Rules can either apply to API resources (such as "pods" or "secrets") or non-resource URL paths (such as "/api"), but not both. + type: array + items: + type: string + resourceNames: + description: ResourceNames is an optional white list of names that the rule applies to. An empty set means that everything is allowed. + type: array + items: + type: string + resources: + description: Resources is a list of resources this rule applies to. '*' represents all resources. + type: array + items: + type: string + verbs: + description: Verbs is a list of Verbs that apply to ALL the ResourceKinds contained in this rule. '*' represents all verbs. + type: array + items: + type: string + serviceAccountName: + type: string + strategy: + type: string + installModes: + description: InstallModes specify supported installation types + type: array + items: + description: InstallMode associates an InstallModeType with a flag representing if the CSV supports it + type: object + required: + - supported + - type + properties: + supported: + type: boolean + type: + description: InstallModeType is a supported type of install mode for CSV installation + type: string + keywords: + description: A list of keywords describing the operator. + type: array + items: + type: string + labels: + description: Map of string keys and values that can be used to organize and categorize (scope and select) objects. + type: object + additionalProperties: + type: string + links: + description: A list of links related to the operator. + type: array + items: + type: object + properties: + name: + type: string + url: + type: string + maintainers: + description: A list of organizational entities maintaining the operator. + type: array + items: + type: object + properties: + email: + type: string + name: + type: string + maturity: + type: string + minKubeVersion: + type: string + nativeAPIs: + type: array + items: + description: GroupVersionKind unambiguously identifies a kind. It doesn't anonymously include GroupVersion to avoid automatic coercion. It doesn't use a GroupVersion to avoid custom marshalling + type: object + required: + - group + - kind + - version + properties: + group: + type: string + kind: + type: string + version: + type: string + provider: + description: The publishing entity behind the operator. + type: object + properties: + name: + type: string + url: + type: string + relatedImages: + description: List any related images, or other container images that your Operator might require to perform their functions. This list should also include operand images as well. All image references should be specified by digest (SHA) and not by tag. This field is only used during catalog creation and plays no part in cluster runtime. + type: array + items: + type: object + required: + - image + - name + properties: + image: + type: string + name: + type: string + replaces: + description: The name of a CSV this one replaces. Should match the `metadata.Name` field of the old CSV. + type: string + selector: + description: Label selector for related resources. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + skips: + description: The name(s) of one or more CSV(s) that should be skipped in the upgrade graph. Should match the `metadata.Name` field of the CSV that should be skipped. This field is only used during catalog creation and plays no part in cluster runtime. + type: array + items: + type: string + version: + type: string + webhookdefinitions: + type: array + items: + description: WebhookDescription provides details to OLM about required webhooks + type: object + required: + - admissionReviewVersions + - generateName + - sideEffects + - type + properties: + admissionReviewVersions: + type: array + items: + type: string + containerPort: + type: integer + format: int32 + default: 443 + maximum: 65535 + minimum: 1 + conversionCRDs: + type: array + items: + type: string + deploymentName: + type: string + failurePolicy: + description: FailurePolicyType specifies a failure policy that defines how unrecognized errors from the admission endpoint are handled. + type: string + generateName: + type: string + matchPolicy: + description: MatchPolicyType specifies the type of match policy. + type: string + objectSelector: + description: A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + reinvocationPolicy: + description: ReinvocationPolicyType specifies what type of policy the admission hook uses. + type: string + rules: + type: array + items: + description: RuleWithOperations is a tuple of Operations and Resources. It is recommended to make sure that all the tuple expansions are valid. + type: object + properties: + apiGroups: + description: APIGroups is the API groups the resources belong to. '*' is all groups. If '*' is present, the length of the slice must be one. Required. + type: array + items: + type: string + x-kubernetes-list-type: atomic + apiVersions: + description: APIVersions is the API versions the resources belong to. '*' is all versions. If '*' is present, the length of the slice must be one. Required. + type: array + items: + type: string + x-kubernetes-list-type: atomic + operations: + description: Operations is the operations the admission hook cares about - CREATE, UPDATE, DELETE, CONNECT or * for all of those operations and any future admission operations that are added. If '*' is present, the length of the slice must be one. Required. + type: array + items: + description: OperationType specifies an operation for a request. + type: string + x-kubernetes-list-type: atomic + resources: + description: "Resources is a list of resources this rule applies to. \n For example: 'pods' means pods. 'pods/log' means the log subresource of pods. '*' means all resources, but not subresources. 'pods/*' means all subresources of pods. '*/scale' means all scale subresources. '*/*' means all resources and their subresources. \n If wildcard is present, the validation rule will ensure resources do not overlap with each other. \n Depending on the enclosing object, subresources might not be allowed. Required." + type: array + items: + type: string + x-kubernetes-list-type: atomic + scope: + description: scope specifies the scope of this rule. Valid values are "Cluster", "Namespaced", and "*" "Cluster" means that only cluster-scoped resources will match this rule. Namespace API objects are cluster-scoped. "Namespaced" means that only namespaced resources will match this rule. "*" means that there are no scope restrictions. Subresources match the scope of their parent resource. Default is "*". + type: string + sideEffects: + description: SideEffectClass specifies the types of side effects a webhook may have. + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + type: integer + format: int32 + type: + description: WebhookAdmissionType is the type of admission webhooks supported by OLM + type: string + enum: + - ValidatingAdmissionWebhook + - MutatingAdmissionWebhook + - ConversionWebhook + webhookPath: + type: string + status: + description: ClusterServiceVersionStatus represents information about the status of a CSV. Status may trail the actual state of a system. + type: object + properties: + certsLastUpdated: + description: Last time the owned APIService certs were updated + type: string + format: date-time + certsRotateAt: + description: Time the owned APIService certs will rotate next + type: string + format: date-time + cleanup: + description: CleanupStatus represents information about the status of cleanup while a CSV is pending deletion + type: object + properties: + pendingDeletion: + description: PendingDeletion is the list of custom resource objects that are pending deletion and blocked on finalizers. This indicates the progress of cleanup that is blocking CSV deletion or operator uninstall. + type: array + items: + description: ResourceList represents a list of resources which are of the same Group/Kind + type: object + required: + - group + - instances + - kind + properties: + group: + type: string + instances: + type: array + items: + type: object + required: + - name + properties: + name: + type: string + namespace: + description: Namespace can be empty for cluster-scoped resources + type: string + kind: + type: string + conditions: + description: List of conditions, a history of state transitions + type: array + items: + description: Conditions appear in the status as a record of state transitions on the ClusterServiceVersion + type: object + properties: + lastTransitionTime: + description: Last time the status transitioned from one status to another. + type: string + format: date-time + lastUpdateTime: + description: Last time we updated the status + type: string + format: date-time + message: + description: A human readable message indicating details about why the ClusterServiceVersion is in this condition. + type: string + phase: + description: Condition of the ClusterServiceVersion + type: string + reason: + description: A brief CamelCase message indicating details about why the ClusterServiceVersion is in this state. e.g. 'RequirementsNotMet' + type: string + lastTransitionTime: + description: Last time the status transitioned from one status to another. + type: string + format: date-time + lastUpdateTime: + description: Last time we updated the status + type: string + format: date-time + message: + description: A human readable message indicating details about why the ClusterServiceVersion is in this condition. + type: string + phase: + description: Current condition of the ClusterServiceVersion + type: string + reason: + description: A brief CamelCase message indicating details about why the ClusterServiceVersion is in this state. e.g. 'RequirementsNotMet' + type: string + requirementStatus: + description: The status of each requirement for this CSV + type: array + items: + type: object + required: + - group + - kind + - message + - name + - status + - version + properties: + dependents: + type: array + items: + description: DependentStatus is the status for a dependent requirement (to prevent infinite nesting) + type: object + required: + - group + - kind + - status + - version + properties: + group: + type: string + kind: + type: string + message: + type: string + status: + description: StatusReason is a camelcased reason for the status of a RequirementStatus or DependentStatus + type: string + uuid: + type: string + version: + type: string + group: + type: string + kind: + type: string + message: + type: string + name: + type: string + status: + description: StatusReason is a camelcased reason for the status of a RequirementStatus or DependentStatus + type: string + uuid: + type: string + version: + type: string + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.0 + creationTimestamp: null + name: installplans.operators.coreos.com +spec: + group: operators.coreos.com + names: + categories: + - olm + kind: InstallPlan + listKind: InstallPlanList + plural: installplans + shortNames: + - ip + singular: installplan + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The first CSV in the list of clusterServiceVersionNames + jsonPath: .spec.clusterServiceVersionNames[0] + name: CSV + type: string + - description: The approval mode + jsonPath: .spec.approval + name: Approval + type: string + - jsonPath: .spec.approved + name: Approved + type: boolean + name: v1alpha1 + schema: + openAPIV3Schema: + description: InstallPlan defines the installation of a set of operators. + type: object + required: + - metadata + - spec + 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: InstallPlanSpec defines a set of Application resources to be installed + type: object + required: + - approval + - approved + - clusterServiceVersionNames + properties: + approval: + description: Approval is the user approval policy for an InstallPlan. It must be one of "Automatic" or "Manual". + type: string + approved: + type: boolean + clusterServiceVersionNames: + type: array + items: + type: string + generation: + type: integer + source: + type: string + sourceNamespace: + type: string + status: + description: "InstallPlanStatus represents the information about the status of steps required to complete installation. \n Status may trail the actual state of a system." + type: object + required: + - catalogSources + - phase + properties: + attenuatedServiceAccountRef: + description: AttenuatedServiceAccountRef references the service account that is used to do scoped operator install. + type: object + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an entire object, this string should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. For example, if the object reference is to a container within a pod, this would take on a value like: "spec.containers{name}" (where "name" refers to the name of the container that triggered the event) or if no container name is specified "spec.containers[2]" (container with index 2 in this pod). This syntax is chosen only to have some well-defined way of referencing a part of an object. TODO: this design is not final and this field is subject to change in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + bundleLookups: + description: BundleLookups is the set of in-progress requests to pull and unpackage bundle content to the cluster. + type: array + items: + description: BundleLookup is a request to pull and unpackage the content of a bundle to the cluster. + type: object + required: + - catalogSourceRef + - identifier + - path + - replaces + properties: + catalogSourceRef: + description: CatalogSourceRef is a reference to the CatalogSource the bundle path was resolved from. + type: object + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an entire object, this string should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. For example, if the object reference is to a container within a pod, this would take on a value like: "spec.containers{name}" (where "name" refers to the name of the container that triggered the event) or if no container name is specified "spec.containers[2]" (container with index 2 in this pod). This syntax is chosen only to have some well-defined way of referencing a part of an object. TODO: this design is not final and this field is subject to change in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + conditions: + description: Conditions represents the overall state of a BundleLookup. + type: array + items: + type: object + required: + - status + - type + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status to another. + type: string + format: date-time + lastUpdateTime: + description: Last time the condition was probed. + type: string + format: date-time + message: + description: A human readable message indicating details about the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + identifier: + description: Identifier is the catalog-unique name of the operator (the name of the CSV for bundles that contain CSVs) + type: string + path: + description: Path refers to the location of a bundle to pull. It's typically an image reference. + type: string + properties: + description: The effective properties of the unpacked bundle. + type: string + replaces: + description: Replaces is the name of the bundle to replace with the one found at Path. + type: string + catalogSources: + type: array + items: + type: string + conditions: + type: array + items: + description: InstallPlanCondition represents the overall status of the execution of an InstallPlan. + type: object + properties: + lastTransitionTime: + type: string + format: date-time + lastUpdateTime: + type: string + format: date-time + message: + type: string + reason: + description: ConditionReason is a camelcased reason for the state transition. + type: string + status: + type: string + type: + description: InstallPlanConditionType describes the state of an InstallPlan at a certain point as a whole. + type: string + message: + description: Message is a human-readable message containing detailed information that may be important to understanding why the plan has its current status. + type: string + phase: + description: InstallPlanPhase is the current status of a InstallPlan as a whole. + type: string + plan: + type: array + items: + description: Step represents the status of an individual step in an InstallPlan. + type: object + required: + - resolving + - resource + - status + properties: + optional: + type: boolean + resolving: + type: string + resource: + description: StepResource represents the status of a resource to be tracked by an InstallPlan. + type: object + required: + - group + - kind + - name + - sourceName + - sourceNamespace + - version + properties: + group: + type: string + kind: + type: string + manifest: + type: string + name: + type: string + sourceName: + type: string + sourceNamespace: + type: string + version: + type: string + status: + description: StepStatus is the current status of a particular resource an in InstallPlan + type: string + startTime: + description: StartTime is the time when the controller began applying the resources listed in the plan to the cluster. + type: string + format: date-time + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.0 + creationTimestamp: null + name: olmconfigs.operators.coreos.com +spec: + group: operators.coreos.com + names: + categories: + - olm + kind: OLMConfig + listKind: OLMConfigList + plural: olmconfigs + singular: olmconfig + scope: Cluster + versions: + - name: v1 + schema: + openAPIV3Schema: + description: OLMConfig is a resource responsible for configuring OLM. + type: object + required: + - metadata + 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: OLMConfigSpec is the spec for an OLMConfig resource. + type: object + properties: + features: + description: Features contains the list of configurable OLM features. + type: object + properties: + disableCopiedCSVs: + description: DisableCopiedCSVs is used to disable OLM's "Copied CSV" feature for operators installed at the cluster scope, where a cluster scoped operator is one that has been installed in an OperatorGroup that targets all namespaces. When reenabled, OLM will recreate the "Copied CSVs" for each cluster scoped operator. + type: boolean + packageServerSyncInterval: + description: PackageServerSyncInterval is used to define the sync interval for packagerserver pods. Packageserver pods periodically check the status of CatalogSources; this specifies the period using duration format (e.g. "60m"). For this parameter, only hours ("h"), minutes ("m"), and seconds ("s") may be specified. When not specified, the period defaults to the value specified within the packageserver. + type: string + pattern: ^([0-9]+(\.[0-9]+)?(s|m|h))+$ + status: + description: OLMConfigStatus is the status for an OLMConfig resource. + type: object + properties: + conditions: + type: array + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + type: object + required: + - lastTransitionTime + - message + - reason + - status + - type + 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. + type: string + format: date-time + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + type: string + maxLength: 32768 + 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. + type: integer + format: int64 + minimum: 0 + 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. + type: string + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + status: + description: status of the condition, one of True, False, Unknown. + type: string + enum: + - "True" + - "False" + - Unknown + 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) + type: string + 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])$ + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.0 + creationTimestamp: null + name: operatorconditions.operators.coreos.com +spec: + group: operators.coreos.com + names: + categories: + - olm + kind: OperatorCondition + listKind: OperatorConditionList + plural: operatorconditions + shortNames: + - condition + singular: operatorcondition + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: OperatorCondition is a Custom Resource of type `OperatorCondition` which is used to convey information to OLM about the state of an operator. + type: object + required: + - metadata + 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: OperatorConditionSpec allows a cluster admin to convey information about the state of an operator to OLM, potentially overriding state reported by the operator. + type: object + properties: + deployments: + type: array + items: + type: string + overrides: + type: array + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + type: object + required: + - message + - reason + - status + - type + 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. + type: string + format: date-time + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + type: string + maxLength: 32768 + 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. + type: integer + format: int64 + minimum: 0 + 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. + type: string + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + status: + description: status of the condition, one of True, False, Unknown. + type: string + enum: + - "True" + - "False" + - Unknown + 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) + type: string + 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])$ + serviceAccounts: + type: array + items: + type: string + status: + description: OperatorConditionStatus allows an operator to convey information its state to OLM. The status may trail the actual state of a system. + type: object + properties: + conditions: + type: array + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + type: object + required: + - lastTransitionTime + - message + - reason + - status + - type + 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. + type: string + format: date-time + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + type: string + maxLength: 32768 + 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. + type: integer + format: int64 + minimum: 0 + 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. + type: string + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + status: + description: status of the condition, one of True, False, Unknown. + type: string + enum: + - "True" + - "False" + - Unknown + 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) + type: string + 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])$ + served: true + storage: false + subresources: + status: {} + - name: v2 + schema: + openAPIV3Schema: + description: OperatorCondition is a Custom Resource of type `OperatorCondition` which is used to convey information to OLM about the state of an operator. + type: object + required: + - metadata + 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: OperatorConditionSpec allows an operator to report state to OLM and provides cluster admin with the ability to manually override state reported by the operator. + type: object + properties: + conditions: + type: array + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + type: object + required: + - lastTransitionTime + - message + - reason + - status + - type + 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. + type: string + format: date-time + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + type: string + maxLength: 32768 + 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. + type: integer + format: int64 + minimum: 0 + 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. + type: string + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + status: + description: status of the condition, one of True, False, Unknown. + type: string + enum: + - "True" + - "False" + - Unknown + 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) + type: string + 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])$ + deployments: + type: array + items: + type: string + overrides: + type: array + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + type: object + required: + - message + - reason + - status + - type + 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. + type: string + format: date-time + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + type: string + maxLength: 32768 + 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. + type: integer + format: int64 + minimum: 0 + 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. + type: string + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + status: + description: status of the condition, one of True, False, Unknown. + type: string + enum: + - "True" + - "False" + - Unknown + 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) + type: string + 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])$ + serviceAccounts: + type: array + items: + type: string + status: + description: OperatorConditionStatus allows OLM to convey which conditions have been observed. + type: object + properties: + conditions: + type: array + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + type: object + required: + - lastTransitionTime + - message + - reason + - status + - type + 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. + type: string + format: date-time + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + type: string + maxLength: 32768 + 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. + type: integer + format: int64 + minimum: 0 + 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. + type: string + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + status: + description: status of the condition, one of True, False, Unknown. + type: string + enum: + - "True" + - "False" + - Unknown + 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) + type: string + 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])$ + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.0 + creationTimestamp: null + name: operatorgroups.operators.coreos.com +spec: + group: operators.coreos.com + names: + categories: + - olm + kind: OperatorGroup + listKind: OperatorGroupList + plural: operatorgroups + shortNames: + - og + singular: operatorgroup + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: OperatorGroup is the unit of multitenancy for OLM managed operators. It constrains the installation of operators in its namespace to a specified set of target namespaces. + type: object + required: + - metadata + 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: OperatorGroupSpec is the spec for an OperatorGroup resource. + type: object + default: + upgradeStrategy: Default + properties: + selector: + description: Selector selects the OperatorGroup's target namespaces. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + serviceAccountName: + description: ServiceAccountName is the admin specified service account which will be used to deploy operator(s) in this operator group. + type: string + staticProvidedAPIs: + description: Static tells OLM not to update the OperatorGroup's providedAPIs annotation + type: boolean + targetNamespaces: + description: TargetNamespaces is an explicit set of namespaces to target. If it is set, Selector is ignored. + type: array + items: + type: string + x-kubernetes-list-type: set + upgradeStrategy: + description: "UpgradeStrategy defines the upgrade strategy for operators in the namespace. There are currently two supported upgrade strategies: \n Default: OLM will only allow clusterServiceVersions to move to the replacing phase from the succeeded phase. This effectively means that OLM will not allow operators to move to the next version if an installation or upgrade has failed. \n TechPreviewUnsafeFailForward: OLM will allow clusterServiceVersions to move to the replacing phase from the succeeded phase or from the failed phase. Additionally, OLM will generate new installPlans when a subscription references a failed installPlan and the catalog has been updated with a new upgrade for the existing set of operators. \n WARNING: The TechPreviewUnsafeFailForward upgrade strategy is unsafe and may result in unexpected behavior or unrecoverable data loss unless you have deep understanding of the set of operators being managed in the namespace." + type: string + default: Default + enum: + - Default + - TechPreviewUnsafeFailForward + status: + description: OperatorGroupStatus is the status for an OperatorGroupResource. + type: object + required: + - lastUpdated + properties: + conditions: + description: Conditions is an array of the OperatorGroup's conditions. + type: array + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + type: object + required: + - lastTransitionTime + - message + - reason + - status + - type + 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. + type: string + format: date-time + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + type: string + maxLength: 32768 + 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. + type: integer + format: int64 + minimum: 0 + 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. + type: string + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + status: + description: status of the condition, one of True, False, Unknown. + type: string + enum: + - "True" + - "False" + - Unknown + 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) + type: string + 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])$ + lastUpdated: + description: LastUpdated is a timestamp of the last time the OperatorGroup's status was Updated. + type: string + format: date-time + namespaces: + description: Namespaces is the set of target namespaces for the OperatorGroup. + type: array + items: + type: string + x-kubernetes-list-type: set + serviceAccountRef: + description: ServiceAccountRef references the service account object specified. + type: object + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an entire object, this string should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. For example, if the object reference is to a container within a pod, this would take on a value like: "spec.containers{name}" (where "name" refers to the name of the container that triggered the event) or if no container name is specified "spec.containers[2]" (container with index 2 in this pod). This syntax is chosen only to have some well-defined way of referencing a part of an object. TODO: this design is not final and this field is subject to change in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + served: true + storage: true + subresources: + status: {} + - name: v1alpha2 + schema: + openAPIV3Schema: + description: OperatorGroup is the unit of multitenancy for OLM managed operators. It constrains the installation of operators in its namespace to a specified set of target namespaces. + type: object + required: + - metadata + 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: OperatorGroupSpec is the spec for an OperatorGroup resource. + type: object + properties: + selector: + description: Selector selects the OperatorGroup's target namespaces. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + serviceAccountName: + description: ServiceAccountName is the admin specified service account which will be used to deploy operator(s) in this operator group. + type: string + staticProvidedAPIs: + description: Static tells OLM not to update the OperatorGroup's providedAPIs annotation + type: boolean + targetNamespaces: + description: TargetNamespaces is an explicit set of namespaces to target. If it is set, Selector is ignored. + type: array + items: + type: string + status: + description: OperatorGroupStatus is the status for an OperatorGroupResource. + type: object + required: + - lastUpdated + properties: + lastUpdated: + description: LastUpdated is a timestamp of the last time the OperatorGroup's status was Updated. + type: string + format: date-time + namespaces: + description: Namespaces is the set of target namespaces for the OperatorGroup. + type: array + items: + type: string + serviceAccountRef: + description: ServiceAccountRef references the service account object specified. + type: object + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an entire object, this string should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. For example, if the object reference is to a container within a pod, this would take on a value like: "spec.containers{name}" (where "name" refers to the name of the container that triggered the event) or if no container name is specified "spec.containers[2]" (container with index 2 in this pod). This syntax is chosen only to have some well-defined way of referencing a part of an object. TODO: this design is not final and this field is subject to change in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + served: true + storage: false + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.0 + creationTimestamp: null + name: operators.operators.coreos.com +spec: + group: operators.coreos.com + names: + categories: + - olm + kind: Operator + listKind: OperatorList + plural: operators + singular: operator + scope: Cluster + versions: + - name: v1 + schema: + openAPIV3Schema: + description: Operator represents a cluster operator. + type: object + 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: OperatorSpec defines the desired state of Operator + type: object + status: + description: OperatorStatus defines the observed state of an Operator and its components + type: object + properties: + components: + description: Components describes resources that compose the operator. + type: object + required: + - labelSelector + properties: + labelSelector: + description: LabelSelector is a label query over a set of resources used to select the operator's components + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + refs: + description: Refs are a set of references to the operator's component resources, selected with LabelSelector. + type: array + items: + description: RichReference is a reference to a resource, enriched with its status conditions. + type: object + properties: + apiVersion: + description: API version of the referent. + type: string + conditions: + description: Conditions represents the latest state of the component. + type: array + items: + description: Condition represent the latest available observations of an component's state. + type: object + required: + - status + - type + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status to another. + type: string + format: date-time + lastUpdateTime: + description: Last time the condition was probed + type: string + format: date-time + message: + description: A human readable message indicating details about the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an entire object, this string should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. For example, if the object reference is to a container within a pod, this would take on a value like: "spec.containers{name}" (where "name" refers to the name of the container that triggered the event) or if no container name is specified "spec.containers[2]" (container with index 2 in this pod). This syntax is chosen only to have some well-defined way of referencing a part of an object. TODO: this design is not final and this field is subject to change in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.0 + creationTimestamp: null + name: subscriptions.operators.coreos.com +spec: + group: operators.coreos.com + names: + categories: + - olm + kind: Subscription + listKind: SubscriptionList + plural: subscriptions + shortNames: + - sub + - subs + singular: subscription + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The package subscribed to + jsonPath: .spec.name + name: Package + type: string + - description: The catalog source for the specified package + jsonPath: .spec.source + name: Source + type: string + - description: The channel of updates to subscribe to + jsonPath: .spec.channel + name: Channel + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: Subscription keeps operators up to date by tracking changes to Catalogs. + type: object + required: + - metadata + - spec + 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: SubscriptionSpec defines an Application that can be installed + type: object + required: + - name + - source + - sourceNamespace + properties: + channel: + type: string + config: + description: SubscriptionConfig contains configuration specified for a subscription. + type: object + properties: + affinity: + description: If specified, overrides the pod's scheduling constraints. nil sub-attributes will *not* override the original values in the pod.spec for those sub-attributes. Use empty object ({}) to erase original sub-attribute values. + type: object + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the pod. + type: object + 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. + type: array + 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). + type: object + required: + - preference + - weight + properties: + preference: + description: A node selector term, associated with the corresponding weight. + type: object + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + type: array + items: + description: A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchFields: + description: A list of node selector requirements by node's fields. + type: array + items: + description: A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + weight: + description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. + type: integer + format: int32 + 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. + type: object + required: + - nodeSelectorTerms + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The terms are ORed. + type: array + 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. + type: object + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + type: array + items: + description: A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchFields: + description: A list of node selector requirements by node's fields. + type: array + items: + description: A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). + type: object + 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. + type: array + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + type: object + required: + - podAffinityTerm + - weight + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query over a set of resources, in this case pods. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + 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. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + 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". + type: array + items: + type: string + 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 + weight: + description: weight associated with matching the corresponding podAffinityTerm, in the range 1-100. + type: integer + format: int32 + 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. + type: array + 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 + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query over a set of resources, in this case pods. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + 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. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + 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". + type: array + items: + type: string + 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 + 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)). + type: object + 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. + type: array + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + type: object + required: + - podAffinityTerm + - weight + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query over a set of resources, in this case pods. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + 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. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + 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". + type: array + items: + type: string + 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 + weight: + description: weight associated with matching the corresponding podAffinityTerm, in the range 1-100. + type: integer + format: int32 + 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. + type: array + 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 + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query over a set of resources, in this case pods. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + 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. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + 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". + type: array + items: + type: string + 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 + annotations: + description: Annotations is an unstructured key value map stored with each Deployment, Pod, APIService in the Operator. Typically, annotations may be set by external tools to store and retrieve arbitrary metadata. Use this field to pre-define annotations that OLM should add to each of the Subscription's deployments, pods, and apiservices. + type: object + additionalProperties: + type: string + env: + description: Env is a list of environment variables to set in the container. Cannot be updated. + type: array + items: + description: EnvVar represents an environment variable present in a Container. + type: object + required: + - name + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's value. Cannot be used if value is not empty. + type: object + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + type: object + required: + - key + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap or its key must be defined + type: boolean + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs.' + type: object + required: + - fieldPath + properties: + apiVersion: + description: Version of the schema the FieldPath is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified API version. + type: string + resourceFieldRef: + description: 'Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported.' + type: object + required: + - resource + properties: + containerName: + description: 'Container name: required for volumes, optional for env vars' + type: string + divisor: + description: Specifies the output format of the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + type: object + required: + - key + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret or its key must be defined + type: boolean + envFrom: + description: EnvFrom is a list of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Immutable. + type: array + items: + description: EnvFromSource represents the source of a set of ConfigMaps + type: object + properties: + configMapRef: + description: The ConfigMap to select from + type: object + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap must be defined + type: boolean + prefix: + description: An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + type: object + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + nodeSelector: + description: 'NodeSelector is a selector which must be true for the pod to fit on a node. Selector which must match a node''s labels for the pod to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' + type: object + additionalProperties: + type: string + resources: + description: 'Resources represents compute resources required by this container. Immutable. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + properties: + claims: + description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." + type: array + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + type: object + required: + - name + properties: + name: + description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. + type: string + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + additionalProperties: + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + requests: + description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + additionalProperties: + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + selector: + description: Selector is the label selector for pods to be configured. Existing ReplicaSets whose pods are selected by this will be the ones affected by this deployment. It must match the pod template's labels. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + tolerations: + description: Tolerations are the pod's tolerations. + type: array + items: + description: The pod this Toleration is attached to tolerates any taint that matches the triple using the matching operator . + type: object + 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. + type: integer + format: int64 + 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 + volumeMounts: + description: List of VolumeMounts to set in the container. + type: array + items: + description: VolumeMount describes a mounting of a Volume within a container. + type: object + required: + - mountPath + - name + properties: + mountPath: + description: Path within the container at which the volume should be mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false. + type: boolean + subPath: + description: Path within the volume from which the container's volume should be mounted. Defaults to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume from which the container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to "" (volume's root). SubPathExpr and SubPath are mutually exclusive. + type: string + volumes: + description: List of Volumes to set in the podSpec. + type: array + items: + description: Volume represents a named volume in a pod that may be accessed by any container in the pod. + type: object + required: + - name + properties: + awsElasticBlockStore: + description: 'awsElasticBlockStore represents an AWS Disk resource that is attached to a kubelet''s host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: object + required: + - volumeID + properties: + fsType: + description: 'fsType is the filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore TODO: how do we prevent errors in the filesystem from compromising the machine' + type: string + partition: + description: 'partition is the partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as "1". Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty).' + type: integer + format: int32 + readOnly: + description: 'readOnly value true will force the readOnly setting in VolumeMounts. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'volumeID is unique ID of the persistent disk resource in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + azureDisk: + description: azureDisk represents an Azure Data Disk mount on the host and bind mount to the pod. + type: object + required: + - diskName + - diskURI + properties: + cachingMode: + description: 'cachingMode is the Host Caching mode: None, Read Only, Read Write.' + type: string + diskName: + description: diskName is the Name of the data disk in the blob storage + type: string + diskURI: + description: diskURI is the URI of data disk in the blob storage + type: string + fsType: + description: fsType is Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + kind: + description: 'kind expected values are Shared: multiple blob disks per storage account Dedicated: single blob disk per storage account Managed: azure managed data disk (only in managed availability set). defaults to shared' + type: string + readOnly: + description: readOnly Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. + type: boolean + azureFile: + description: azureFile represents an Azure File Service mount on the host and bind mount to the pod. + type: object + required: + - secretName + - shareName + properties: + readOnly: + description: readOnly defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: secretName is the name of secret that contains Azure Storage Account Name and Key + type: string + shareName: + description: shareName is the azure share Name + type: string + cephfs: + description: cephFS represents a Ceph FS mount on the host that shares a pod's lifetime + type: object + required: + - monitors + properties: + monitors: + description: 'monitors is Required: Monitors is a collection of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: array + items: + type: string + path: + description: 'path is Optional: Used as the mounted root, rather than the full Ceph tree, default is /' + type: string + readOnly: + description: 'readOnly is Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'secretFile is Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'secretRef is Optional: SecretRef is reference to the authentication secret for User, default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: object + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + user: + description: 'user is optional: User is the rados user name, default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + cinder: + description: 'cinder represents a cinder volume attached and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: object + required: + - volumeID + properties: + fsType: + description: 'fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'readOnly defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'secretRef is optional: points to a secret object containing parameters used to connect to OpenStack.' + type: object + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + volumeID: + description: 'volumeID used to identify the volume in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + configMap: + description: configMap represents a configMap that should populate this volume + type: object + properties: + defaultMode: + description: 'defaultMode is optional: mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.' + type: integer + format: int32 + items: + description: items if unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the ConfigMap, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.' + type: integer + format: int32 + path: + description: path is the relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: optional specify whether the ConfigMap or its keys must be defined + type: boolean + csi: + description: csi (Container Storage Interface) represents ephemeral storage that is handled by certain external CSI drivers (Beta feature). + type: object + required: + - driver + properties: + driver: + description: driver is the name of the CSI driver that handles this volume. Consult with your admin for the correct name as registered in the cluster. + type: string + fsType: + description: fsType to mount. Ex. "ext4", "xfs", "ntfs". If not provided, the empty value is passed to the associated CSI driver which will determine the default filesystem to apply. + type: string + nodePublishSecretRef: + description: nodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secret references are passed. + type: object + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + readOnly: + description: readOnly specifies a read-only configuration for the volume. Defaults to false (read/write). + type: boolean + volumeAttributes: + description: volumeAttributes stores driver-specific properties that are passed to the CSI driver. Consult your driver's documentation for supported values. + type: object + additionalProperties: + type: string + downwardAPI: + description: downwardAPI represents downward API about the pod that should populate this volume + type: object + properties: + defaultMode: + description: 'Optional: mode bits to use on created files by default. Must be a Optional: mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.' + type: integer + format: int32 + items: + description: Items is a list of downward API volume file + type: array + items: + description: DownwardAPIVolumeFile represents information to create the file containing the pod field + type: object + required: + - path + properties: + fieldRef: + description: 'Required: Selects a field of the pod: only annotations, labels, name and namespace are supported.' + type: object + required: + - fieldPath + properties: + apiVersion: + description: Version of the schema the FieldPath is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified API version. + type: string + mode: + description: 'Optional: mode bits used to set permissions on this file, must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.' + type: integer + format: int32 + path: + description: 'Required: Path is the relative path name of the file to be created. Must not be absolute or contain the ''..'' path. Must be utf-8 encoded. The first item of the relative path must not start with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported.' + type: object + required: + - resource + properties: + containerName: + description: 'Container name: required for volumes, optional for env vars' + type: string + divisor: + description: Specifies the output format of the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + emptyDir: + description: 'emptyDir represents a temporary directory that shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: object + properties: + medium: + description: 'medium represents what type of storage medium should back this directory. The default is "" which means to use the node''s default medium. Must be an empty string (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: string + sizeLimit: + description: 'sizeLimit is the total amount of local storage required for this EmptyDir volume. The size limit is also applicable for memory medium. The maximum usage on memory medium EmptyDir would be the minimum value between the SizeLimit specified here and the sum of memory limits of all containers in a pod. The default is nil which means that the limit is undefined. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + ephemeral: + description: "ephemeral represents a volume that is handled by a cluster storage driver. The volume's lifecycle is tied to the pod that defines it - it will be created before the pod starts, and deleted when the pod is removed. \n Use this if: a) the volume is only needed while the pod runs, b) features of normal volumes like restoring from snapshot or capacity tracking are needed, c) the storage driver is specified through a storage class, and d) the storage driver supports dynamic volume provisioning through a PersistentVolumeClaim (see EphemeralVolumeSource for more information on the connection between this volume type and PersistentVolumeClaim). \n Use PersistentVolumeClaim or one of the vendor-specific APIs for volumes that persist for longer than the lifecycle of an individual pod. \n Use CSI for light-weight local ephemeral volumes if the CSI driver is meant to be used that way - see the documentation of the driver for more information. \n A pod can use both types of ephemeral volumes and persistent volumes at the same time." + type: object + properties: + volumeClaimTemplate: + description: "Will be used to create a stand-alone PVC to provision the volume. The pod in which this EphemeralVolumeSource is embedded will be the owner of the PVC, i.e. the PVC will be deleted together with the pod. The name of the PVC will be `-` where `` is the name from the `PodSpec.Volumes` array entry. Pod validation will reject the pod if the concatenated name is not valid for a PVC (for example, too long). \n An existing PVC with that name that is not owned by the pod will *not* be used for the pod to avoid using an unrelated volume by mistake. Starting the pod is then blocked until the unrelated PVC is removed. If such a pre-created PVC is meant to be used by the pod, the PVC has to updated with an owner reference to the pod once the pod exists. Normally this should not be necessary, but it may be useful when manually reconstructing a broken cluster. \n This field is read-only and no changes will be made by Kubernetes to the PVC after it has been created. \n Required, must not be nil." + type: object + required: + - spec + properties: + metadata: + description: May contain labels and annotations that will be copied into the PVC when creating it. No other fields are allowed and will be rejected during validation. + type: object + spec: + description: The specification for the PersistentVolumeClaim. The entire content is copied unchanged into the PVC that gets created from this template. The same fields as in a PersistentVolumeClaim are also valid here. + type: object + properties: + accessModes: + description: 'accessModes contains the desired access modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + type: array + items: + type: string + dataSource: + description: 'dataSource field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) * An existing PVC (PersistentVolumeClaim) If the provisioner or an external controller can support the specified data source, it will create a new volume based on the contents of the specified data source. When the AnyVolumeDataSource feature gate is enabled, dataSource contents will be copied to dataSourceRef, and dataSourceRef contents will be copied to dataSource when dataSourceRef.namespace is not specified. If the namespace is specified, then dataSourceRef will not be copied to dataSource.' + type: object + required: + - kind + - name + properties: + apiGroup: + description: APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being referenced + type: string + name: + description: Name is the name of resource being referenced + type: string + dataSourceRef: + description: 'dataSourceRef specifies the object from which to populate the volume with data, if a non-empty volume is desired. This may be any object from a non-empty API group (non core object) or a PersistentVolumeClaim object. When this field is specified, volume binding will only succeed if the type of the specified object matches some installed volume populator or dynamic provisioner. This field will replace the functionality of the dataSource field and as such if both fields are non-empty, they must have the same value. For backwards compatibility, when namespace isn''t specified in dataSourceRef, both fields (dataSource and dataSourceRef) will be set to the same value automatically if one of them is empty and the other is non-empty. When namespace is specified in dataSourceRef, dataSource isn''t set to the same value and must be empty. There are three important differences between dataSource and dataSourceRef: * While dataSource only allows two specific types of objects, dataSourceRef allows any non-core object, as well as PersistentVolumeClaim objects. * While dataSource ignores disallowed values (dropping them), dataSourceRef preserves all values, and generates an error if a disallowed value is specified. * While dataSource only allows local objects, dataSourceRef allows objects in any namespaces. (Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled. (Alpha) Using the namespace field of dataSourceRef requires the CrossNamespaceVolumeDataSource feature gate to be enabled.' + type: object + required: + - kind + - name + properties: + apiGroup: + description: APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being referenced + type: string + name: + description: Name is the name of resource being referenced + type: string + namespace: + description: Namespace is the namespace of resource being referenced Note that when a namespace is specified, a gateway.networking.k8s.io/ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. (Alpha) This field requires the CrossNamespaceVolumeDataSource feature gate to be enabled. + type: string + resources: + description: 'resources represents the minimum resources the volume should have. If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements that are lower than previous value but must still be higher than capacity recorded in the status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + type: object + properties: + claims: + description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." + type: array + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + type: object + required: + - name + properties: + name: + description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. + type: string + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + additionalProperties: + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + requests: + description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + additionalProperties: + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + selector: + description: selector is a label query over volumes to consider for binding. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + 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. + type: array + items: + type: string + matchLabels: + 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 + additionalProperties: + type: string + storageClassName: + description: 'storageClassName is the name of the StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + type: string + volumeMode: + description: volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. + type: string + volumeName: + description: volumeName is the binding reference to the PersistentVolume backing this claim. + type: string + fc: + description: fc represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod. + type: object + properties: + fsType: + description: 'fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. TODO: how do we prevent errors in the filesystem from compromising the machine' + type: string + lun: + description: 'lun is Optional: FC target lun number' + type: integer + format: int32 + readOnly: + description: 'readOnly is Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.' + type: boolean + targetWWNs: + description: 'targetWWNs is Optional: FC target worldwide names (WWNs)' + type: array + items: + type: string + wwids: + description: 'wwids Optional: FC volume world wide identifiers (wwids) Either wwids or combination of targetWWNs and lun must be set, but not both simultaneously.' + type: array + items: + type: string + flexVolume: + description: flexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin. + type: object + required: + - driver + properties: + driver: + description: driver is the name of the driver to use for this volume. + type: string + fsType: + description: fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". The default filesystem depends on FlexVolume script. + type: string + options: + description: 'options is Optional: this field holds extra command options if any.' + type: object + additionalProperties: + type: string + readOnly: + description: 'readOnly is Optional: defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.' + type: boolean + secretRef: + description: 'secretRef is Optional: secretRef is reference to the secret object containing sensitive information to pass to the plugin scripts. This may be empty if no secret object is specified. If the secret object contains more than one secret, all secrets are passed to the plugin scripts.' + type: object + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + flocker: + description: flocker represents a Flocker volume attached to a kubelet's host machine. This depends on the Flocker control service being running + type: object + properties: + datasetName: + description: datasetName is Name of the dataset stored as metadata -> name on the dataset for Flocker should be considered as deprecated + type: string + datasetUUID: + description: datasetUUID is the UUID of the dataset. This is unique identifier of a Flocker dataset + type: string + gcePersistentDisk: + description: 'gcePersistentDisk represents a GCE Disk resource that is attached to a kubelet''s host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: object + required: + - pdName + properties: + fsType: + description: 'fsType is filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk TODO: how do we prevent errors in the filesystem from compromising the machine' + type: string + partition: + description: 'partition is the partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as "1". Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: integer + format: int32 + pdName: + description: 'pdName is unique name of the PD resource in GCE. Used to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'readOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + gitRepo: + description: 'gitRepo represents a git repository at a particular revision. DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir into the Pod''s container.' + type: object + required: + - repository + properties: + directory: + description: directory is the target directory name. Must not contain or start with '..'. If '.' is supplied, the volume directory will be the git repository. Otherwise, if specified, the volume will contain the git repository in the subdirectory with the given name. + type: string + repository: + description: repository is the URL + type: string + revision: + description: revision is the commit hash for the specified revision. + type: string + glusterfs: + description: 'glusterfs represents a Glusterfs mount on the host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + type: object + required: + - endpoints + - path + properties: + endpoints: + description: 'endpoints is the endpoint name that details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'path is the Glusterfs volume path. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'readOnly here will force the Glusterfs volume to be mounted with read-only permissions. Defaults to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + hostPath: + description: 'hostPath represents a pre-existing file or directory on the host machine that is directly exposed to the container. This is generally used for system agents or other privileged things that are allowed to see the host machine. Most containers will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath --- TODO(jonesdl) We need to restrict who can use host directory mounts and who can/can not mount host directories as read/write.' + type: object + required: + - path + properties: + path: + description: 'path of the directory on the host. If the path is a symlink, it will follow the link to the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'type for HostPath Volume Defaults to "" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + iscsi: + description: 'iscsi represents an ISCSI Disk resource that is attached to a kubelet''s host machine and then exposed to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + type: object + required: + - iqn + - lun + - targetPortal + properties: + chapAuthDiscovery: + description: chapAuthDiscovery defines whether support iSCSI Discovery CHAP authentication + type: boolean + chapAuthSession: + description: chapAuthSession defines whether support iSCSI Session CHAP authentication + type: boolean + fsType: + description: 'fsType is the filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi TODO: how do we prevent errors in the filesystem from compromising the machine' + type: string + initiatorName: + description: initiatorName is the custom iSCSI Initiator Name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface : will be created for the connection. + type: string + iqn: + description: iqn is the target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iscsiInterface is the interface Name that uses an iSCSI transport. Defaults to 'default' (tcp). + type: string + lun: + description: lun represents iSCSI Target Lun number. + type: integer + format: int32 + portals: + description: portals is the iSCSI Target Portal List. The portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260). + type: array + items: + type: string + readOnly: + description: readOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: secretRef is the CHAP Secret for iSCSI target and initiator authentication + type: object + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + targetPortal: + description: targetPortal is iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260). + type: string + name: + description: 'name of the volume. Must be a DNS_LABEL and unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'nfs represents an NFS mount on the host that shares a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: object + required: + - path + - server + properties: + path: + description: 'path that is exported by the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'readOnly here will force the NFS export to be mounted with read-only permissions. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'server is the hostname or IP address of the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + persistentVolumeClaim: + description: 'persistentVolumeClaimVolumeSource represents a reference to a PersistentVolumeClaim in the same namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: object + required: + - claimName + properties: + claimName: + description: 'claimName is the name of a PersistentVolumeClaim in the same namespace as the pod using this volume. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: readOnly Will force the ReadOnly setting in VolumeMounts. Default false. + type: boolean + photonPersistentDisk: + description: photonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine + type: object + required: + - pdID + properties: + fsType: + description: fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + pdID: + description: pdID is the ID that identifies Photon Controller persistent disk + type: string + portworxVolume: + description: portworxVolume represents a portworx volume attached and mounted on kubelets host machine + type: object + required: + - volumeID + properties: + fsType: + description: fSType represents the filesystem type to mount Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs". Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: readOnly defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: volumeID uniquely identifies a Portworx volume + type: string + projected: + description: projected items for all in one resources secrets, configmaps, and downward API + type: object + properties: + defaultMode: + description: defaultMode are the mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + sources: + description: sources is the list of volume projections + type: array + items: + description: Projection that may be projected along with other supported volume types + type: object + properties: + configMap: + description: configMap information about the configMap data to project + type: object + properties: + items: + description: items if unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the ConfigMap, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.' + type: integer + format: int32 + path: + description: path is the relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: optional specify whether the ConfigMap or its keys must be defined + type: boolean + downwardAPI: + description: downwardAPI information about the downwardAPI data to project + type: object + properties: + items: + description: Items is a list of DownwardAPIVolume file + type: array + items: + description: DownwardAPIVolumeFile represents information to create the file containing the pod field + type: object + required: + - path + properties: + fieldRef: + description: 'Required: Selects a field of the pod: only annotations, labels, name and namespace are supported.' + type: object + required: + - fieldPath + properties: + apiVersion: + description: Version of the schema the FieldPath is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified API version. + type: string + mode: + description: 'Optional: mode bits used to set permissions on this file, must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.' + type: integer + format: int32 + path: + description: 'Required: Path is the relative path name of the file to be created. Must not be absolute or contain the ''..'' path. Must be utf-8 encoded. The first item of the relative path must not start with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported.' + type: object + required: + - resource + properties: + containerName: + description: 'Container name: required for volumes, optional for env vars' + type: string + divisor: + description: Specifies the output format of the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + secret: + description: secret information about the secret data to project + type: object + properties: + items: + description: items if unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the Secret, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.' + type: integer + format: int32 + path: + description: path is the relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: optional field specify whether the Secret or its key must be defined + type: boolean + serviceAccountToken: + description: serviceAccountToken is information about the serviceAccountToken data to project + type: object + required: + - path + properties: + audience: + description: audience is the intended audience of the token. A recipient of a token must identify itself with an identifier specified in the audience of the token, and otherwise should reject the token. The audience defaults to the identifier of the apiserver. + type: string + expirationSeconds: + description: expirationSeconds is the requested duration of validity of the service account token. As the token approaches expiration, the kubelet volume plugin will proactively rotate the service account token. The kubelet will start trying to rotate the token if the token is older than 80 percent of its time to live or if the token is older than 24 hours.Defaults to 1 hour and must be at least 10 minutes. + type: integer + format: int64 + path: + description: path is the path relative to the mount point of the file to project the token into. + type: string + quobyte: + description: quobyte represents a Quobyte mount on the host that shares a pod's lifetime + type: object + required: + - registry + - volume + properties: + group: + description: group to map volume access to Default is no group + type: string + readOnly: + description: readOnly here will force the Quobyte volume to be mounted with read-only permissions. Defaults to false. + type: boolean + registry: + description: registry represents a single or multiple Quobyte Registry services specified as a string as host:port pair (multiple entries are separated with commas) which acts as the central registry for volumes + type: string + tenant: + description: tenant owning the given Quobyte volume in the Backend Used with dynamically provisioned Quobyte volumes, value is set by the plugin + type: string + user: + description: user to map volume access to Defaults to serivceaccount user + type: string + volume: + description: volume is a string that references an already created Quobyte volume by name. + type: string + rbd: + description: 'rbd represents a Rados Block Device mount on the host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md' + type: object + required: + - image + - monitors + properties: + fsType: + description: 'fsType is the filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd TODO: how do we prevent errors in the filesystem from compromising the machine' + type: string + image: + description: 'image is the rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'keyring is the path to key ring for RBDUser. Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'monitors is a collection of Ceph monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: array + items: + type: string + pool: + description: 'pool is the rados pool name. Default is rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'readOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'secretRef is name of the authentication secret for RBDUser. If provided overrides keyring. Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: object + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + user: + description: 'user is the rados user name. Default is admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + scaleIO: + description: scaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes. + type: object + required: + - gateway + - secretRef + - system + properties: + fsType: + description: fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Default is "xfs". + type: string + gateway: + description: gateway is the host address of the ScaleIO API Gateway. + type: string + protectionDomain: + description: protectionDomain is the name of the ScaleIO Protection Domain for the configured storage. + type: string + readOnly: + description: readOnly Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: secretRef references to the secret for ScaleIO user and other sensitive information. If this is not provided, Login operation will fail. + type: object + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + sslEnabled: + description: sslEnabled Flag enable/disable SSL communication with Gateway, default false + type: boolean + storageMode: + description: storageMode indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned. + type: string + storagePool: + description: storagePool is the ScaleIO Storage Pool associated with the protection domain. + type: string + system: + description: system is the name of the storage system as configured in ScaleIO. + type: string + volumeName: + description: volumeName is the name of a volume already created in the ScaleIO system that is associated with this volume source. + type: string + secret: + description: 'secret represents a secret that should populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: object + properties: + defaultMode: + description: 'defaultMode is Optional: mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.' + type: integer + format: int32 + items: + description: items If unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the Secret, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.' + type: integer + format: int32 + path: + description: path is the relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'. + type: string + optional: + description: optional field specify whether the Secret or its keys must be defined + type: boolean + secretName: + description: 'secretName is the name of the secret in the pod''s namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + storageos: + description: storageOS represents a StorageOS volume attached and mounted on Kubernetes nodes. + type: object + properties: + fsType: + description: fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: readOnly defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: secretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted. + type: object + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + volumeName: + description: volumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace. + type: string + volumeNamespace: + description: volumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to "default" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created. + type: string + vsphereVolume: + description: vsphereVolume represents a vSphere volume attached and mounted on kubelets host machine + type: object + required: + - volumePath + properties: + fsType: + description: fsType is filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + storagePolicyID: + description: storagePolicyID is the storage Policy Based Management (SPBM) profile ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: storagePolicyName is the storage Policy Based Management (SPBM) profile name. + type: string + volumePath: + description: volumePath is the path that identifies vSphere volume vmdk + type: string + installPlanApproval: + description: Approval is the user approval policy for an InstallPlan. It must be one of "Automatic" or "Manual". + type: string + name: + type: string + source: + type: string + sourceNamespace: + type: string + startingCSV: + type: string + status: + type: object + required: + - lastUpdated + properties: + catalogHealth: + description: CatalogHealth contains the Subscription's view of its relevant CatalogSources' status. It is used to determine SubscriptionStatusConditions related to CatalogSources. + type: array + items: + description: SubscriptionCatalogHealth describes the health of a CatalogSource the Subscription knows about. + type: object + required: + - catalogSourceRef + - healthy + - lastUpdated + properties: + catalogSourceRef: + description: CatalogSourceRef is a reference to a CatalogSource. + type: object + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an entire object, this string should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. For example, if the object reference is to a container within a pod, this would take on a value like: "spec.containers{name}" (where "name" refers to the name of the container that triggered the event) or if no container name is specified "spec.containers[2]" (container with index 2 in this pod). This syntax is chosen only to have some well-defined way of referencing a part of an object. TODO: this design is not final and this field is subject to change in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + healthy: + description: Healthy is true if the CatalogSource is healthy; false otherwise. + type: boolean + lastUpdated: + description: LastUpdated represents the last time that the CatalogSourceHealth changed + type: string + format: date-time + conditions: + description: Conditions is a list of the latest available observations about a Subscription's current state. + type: array + items: + description: SubscriptionCondition represents the latest available observations of a Subscription's state. + type: object + required: + - status + - type + properties: + lastHeartbeatTime: + description: LastHeartbeatTime is the last time we got an update on a given condition + type: string + format: date-time + lastTransitionTime: + description: LastTransitionTime is the last time the condition transit from one status to another + type: string + format: date-time + message: + description: Message is a human-readable message indicating details about last transition. + type: string + reason: + description: Reason is a one-word CamelCase reason for the condition's last transition. + type: string + status: + description: Status is the status of the condition, one of True, False, Unknown. + type: string + type: + description: Type is the type of Subscription condition. + type: string + currentCSV: + description: CurrentCSV is the CSV the Subscription is progressing to. + type: string + installPlanGeneration: + description: InstallPlanGeneration is the current generation of the installplan + type: integer + installPlanRef: + description: InstallPlanRef is a reference to the latest InstallPlan that contains the Subscription's current CSV. + type: object + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an entire object, this string should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. For example, if the object reference is to a container within a pod, this would take on a value like: "spec.containers{name}" (where "name" refers to the name of the container that triggered the event) or if no container name is specified "spec.containers[2]" (container with index 2 in this pod). This syntax is chosen only to have some well-defined way of referencing a part of an object. TODO: this design is not final and this field is subject to change in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + installedCSV: + description: InstalledCSV is the CSV currently installed by the Subscription. + type: string + installplan: + description: 'Install is a reference to the latest InstallPlan generated for the Subscription. DEPRECATED: InstallPlanRef' + type: object + required: + - apiVersion + - kind + - name + - uuid + properties: + apiVersion: + type: string + kind: + type: string + name: + type: string + uuid: + description: UID is a type that holds unique ID values, including UUIDs. Because we don't ONLY use UUIDs, this is an alias to string. Being a type captures intent and helps make sure that UIDs and names do not get conflated. + type: string + lastUpdated: + description: LastUpdated represents the last time that the Subscription status was updated. + type: string + format: date-time + reason: + description: Reason is the reason the Subscription was transitioned to its current state. + type: string + state: + description: State represents the current state of the Subscription + type: string + served: true + storage: true + subresources: + status: {} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/crds/vizier_crd.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/crds/vizier_crd.yaml new file mode 100644 index 0000000000..b25d7b5924 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/crds/vizier_crd.yaml @@ -0,0 +1,347 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.4.1 + creationTimestamp: null + name: viziers.px.dev +spec: + group: px.dev + names: + kind: Vizier + listKind: VizierList + plural: viziers + singular: vizier + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Vizier is the Schema for the viziers 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: VizierSpec defines the desired state of Vizier + properties: + autopilot: + description: Autopilot should be set if running Pixie on GKE Autopilot. + type: boolean + clockConverter: + description: ClockConverter specifies which routine to use for converting + timestamps to a synced reference time. + enum: + - default + - grpc + type: string + cloudAddr: + description: CloudAddr is the address of the cloud instance that the + Vizier should be pointing to. + type: string + clusterName: + description: ClusterName is a name for the Vizier instance, usually + specifying which cluster the Vizier is deployed to. If not specified, + a random name will be generated. + type: string + customDeployKeySecret: + description: CustomDeployKeySecret is the name of the secret where + the deploy key is stored. + type: string + dataAccess: + description: DataAccess defines the level of data that may be accesssed + when executing a script on the cluster. If none specified, assumes + full data access. + enum: + - Full + - Restricted + type: string + dataCollectorParams: + description: DataCollectorParams specifies the set of params for configuring + the dataCollector. If no params are specified, defaults are used. + properties: + customPEMFlags: + additionalProperties: + type: string + description: This contains custom flags that should be passed + to the PEM via environment variables. + type: object + datastreamBufferSize: + description: DatastreamBufferSize is the data buffer size per + connection. Default size is 1 Mbyte. For high-throughput applications, + try increasing this number if experiencing data loss. + format: int32 + type: integer + datastreamBufferSpikeSize: + description: DatastreamBufferSpikeSize is the maximum temporary + size of a data stream buffer before processing. + format: int32 + type: integer + type: object + deployKey: + description: DeployKey is the deploy key associated with the Vizier + instance. This is used to link the Vizier to a specific user/org. + This is required unless specifying a CustomDeployKeySecret. + type: string + devCloudNamespace: + description: 'DevCloudNamespace should be specified only for dev versions + of Pixie cloud which have no ingress to help redirect traffic to + the correct service. The DevCloudNamespace is the namespace that + the dev Pixie cloud is running on, for example: "plc-dev".' + type: string + disableAutoUpdate: + description: DisableAutoUpdate specifies whether auto update should + be enabled for the Vizier instance. + type: boolean + leadershipElectionParams: + description: LeadershipElectionParams specifies configurable values + for the K8s leaderships elections which Vizier uses manage pod leadership. + properties: + electionPeriodMs: + description: ElectionPeriodMs defines how frequently Vizier attempts + to run a K8s leader election, in milliseconds. The period also + determines how long Vizier waits for a leader election response + back from the K8s API. If the K8s API is slow to respond, consider + increasing this number. + format: int64 + type: integer + type: object + patches: + additionalProperties: + type: string + description: Patches defines patches that should be applied to Vizier + resources. The key of the patch should be the name of the resource + that is patched. The value of the patch is the patch, encoded as + a string which follow the "strategic merge patch" rules for K8s. + type: object + pemMemoryLimit: + description: PemMemoryLimit is a memory limit applied specifically + to PEM pods. + type: string + pemMemoryRequest: + description: PemMemoryRequest is a memory request applied specifically + to PEM pods. It will automatically use the value of pemMemoryLimit + if not specified. + type: string + pod: + description: Pod defines the policy for creating Vizier pods. + properties: + annotations: + additionalProperties: + type: string + description: Annotations specifies the annotations to attach to + pods the operator creates. + type: object + labels: + additionalProperties: + type: string + description: Labels specifies the labels to attach to pods the + operator creates. + type: object + nodeSelector: + additionalProperties: + type: string + description: 'NodeSelector is a selector which must be true for + the pod to fit on a node. Selector which must match a node''s + labels for the pod to be scheduled on that node. More info: + https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ + This field cannot be updated once the cluster is created.' + type: object + resources: + description: Resources is the resource requirements for a container. + This field cannot be updated once the cluster is created. + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate. \n This field is immutable." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in + pod.spec.resourceClaims of the Pod where this field + is used. It makes that resource available inside a + container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + 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 + securityContext: + description: The securityContext which should be set on non-privileged + pods. All pods which require privileged permissions will still + require a privileged securityContext. + properties: + enabled: + description: Whether a securityContext should be set on the + pod. In cases where no PSPs are applied to the cluster, + this is not necessary. + type: boolean + fsGroup: + description: A special supplemental group that applies to + all containers in a pod. + format: int64 + type: integer + runAsGroup: + description: The GID to run the entrypoint of the container + process. + format: int64 + type: integer + runAsUser: + description: The UID to run the entrypoint of the container + process. + format: int64 + type: integer + type: object + tolerations: + description: 'Tolerations allows scheduling pods on nodes with + matching taints. More info: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/: + This field cannot be updated once the cluster is created.' + items: + description: The pod this Toleration is attached to tolerates + any taint that matches the triple using + the matching operator . + properties: + effect: + description: Effect indicates the taint effect to match. + Empty means match all taint effects. When specified, allowed + values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, + operator must be Exists; this combination means to match + all values and all keys. + type: string + operator: + description: Operator represents a key's relationship to + the value. Valid operators are Exists and Equal. Defaults + to Equal. Exists is equivalent to wildcard for value, + so that a pod can tolerate all taints of a particular + category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of + time the toleration (which must be of effect NoExecute, + otherwise this field is ignored) tolerates the taint. + By default, it is not set, which means tolerate the taint + forever (do not evict). Zero and negative values will + be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches + to. If the operator is Exists, the value should be empty, + otherwise just a regular string. + type: string + type: object + type: array + type: object + registry: + description: 'Registry specifies the image registry to use rather + than Pixie''s default registry (gcr.io). We expect any forward slashes + in Pixie''s image paths are replaced with a "-". For example: "gcr.io/pixie-oss/pixie-dev/vizier/metadata_server_image:latest" + should be pushed to "$registry/gcr.io-pixie-oss-pixie-dev-vizier-metadata_server_image:latest".' + type: string + useEtcdOperator: + description: UseEtcdOperator specifies whether the metadata service + should use etcd for storage. + type: boolean + version: + description: Version is the desired version of the Vizier instance. + type: string + type: object + status: + description: VizierStatus defines the observed state of Vizier + properties: + checksum: + description: A checksum of the last reconciled Vizier spec. If this + checksum does not match the checksum of the current vizier spec, + reconciliation should be performed. + format: byte + type: string + lastReconciliationPhaseTime: + description: LastReconciliationPhaseTime is the last time that the + ReconciliationPhase changed. + format: date-time + type: string + message: + description: Message is a human-readable message with details about + why the Vizier is in this condition. + type: string + operatorVersion: + description: OperatorVersion is the actual version of the Operator + instance. + type: string + reconciliationPhase: + description: ReconciliationPhase describes the state the Reconciler + is in for this Vizier. See the documentation above the ReconciliationPhase + type for more information. + type: string + sentryDSN: + description: SentryDSN is key for Viziers that is used to send errors + and stacktraces to Sentry. + type: string + version: + description: Version is the actual version of the Vizier instance. + type: string + vizierPhase: + description: VizierPhase is a high-level summary of where the Vizier + is in its lifecycle. + type: string + vizierReason: + description: VizierReason is a short, machine understandable string + that gives the reason for the transition into the Vizier's current + status. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/templates/00_olm.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/templates/00_olm.yaml new file mode 100644 index 0000000000..fe058140f1 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/templates/00_olm.yaml @@ -0,0 +1,232 @@ +{{- $olmCRDFound := false }} +{{- $nsLookup := len (lookup "v1" "Namespace" "" "") }} +{{- range $index, $crdLookup := (lookup "apiextensions.k8s.io/v1" "CustomResourceDefinition" "" "").items -}}{{ if eq $crdLookup.metadata.name "operators.operators.coreos.com"}}{{ $olmCRDFound = true }}{{ end }}{{end}} +{{ if and (not $olmCRDFound) (not (eq $nsLookup 0))}}{{ fail "CRDs missing! Please deploy CRDs from https://github.com/pixie-io/pixie/tree/main/k8s/operator/helm/crds to continue with deploy." }}{{end}} +{{- $lookupLen := 0 -}}{{- $opLookup := (lookup "operators.coreos.com/v1" "OperatorGroup" "" "").items -}}{{if $opLookup }}{{ $lookupLen = len $opLookup }}{{ end }} +{{ if (or (eq (.Values.deployOLM | toString) "true") (and (not (eq (.Values.deployOLM | toString) "false")) (eq $lookupLen 0))) }} +{{ if not (eq .Values.olmNamespace .Release.Namespace) }} +--- +apiVersion: v1 +kind: Namespace +metadata: + name: {{ .Values.olmNamespace }} +{{ end }} +--- +kind: ServiceAccount +apiVersion: v1 +metadata: + name: olm-operator-serviceaccount + namespace: {{ .Values.olmNamespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: system:controller:operator-lifecycle-manager +rules: +- apiGroups: ["*"] + resources: ["*"] + verbs: ["*"] +- nonResourceURLs: ["*"] + verbs: ["*"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: olm-operator-cluster-binding-olm +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:controller:operator-lifecycle-manager +subjects: +- kind: ServiceAccount + name: olm-operator-serviceaccount + namespace: {{ .Values.olmNamespace }} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: olm-operator + namespace: {{ .Values.olmNamespace }} + labels: + app: olm-operator +spec: + strategy: + type: RollingUpdate + replicas: 1 + selector: + matchLabels: + app: olm-operator + template: + metadata: + labels: + app: olm-operator + spec: + serviceAccountName: olm-operator-serviceaccount + containers: + - name: olm-operator + command: + - /bin/olm + args: + - --namespace + - $(OPERATOR_NAMESPACE) + - --writeStatusName + - "" + image: {{ if .Values.registry }}{{ .Values.registry }}/quay.io-operator-framework-{{ else }}quay.io/operator-framework/{{ end }}olm@sha256:1b6002156f568d722c29138575733591037c24b4bfabc67946f268ce4752c3e6 + ports: + - containerPort: 8080 + - containerPort: 8081 + name: metrics + protocol: TCP + livenessProbe: + httpGet: + path: /healthz + port: 8080 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + terminationMessagePolicy: FallbackToLogsOnError + env: + - name: OPERATOR_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: OPERATOR_NAME + value: olm-operator + resources: + requests: + cpu: 10m + memory: 160Mi + nodeSelector: + kubernetes.io/os: linux + tolerations: + - key: "kubernetes.io/arch" + operator: "Equal" + value: "amd64" + effect: "NoSchedule" + - key: "kubernetes.io/arch" + operator: "Equal" + value: "amd64" + effect: "NoExecute" + - key: "kubernetes.io/arch" + operator: "Equal" + value: "arm64" + effect: "NoSchedule" + - key: "kubernetes.io/arch" + operator: "Equal" + value: "arm64" + effect: "NoExecute" +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: catalog-operator + namespace: {{ .Values.olmNamespace }} + labels: + app: catalog-operator +spec: + strategy: + type: RollingUpdate + replicas: 1 + selector: + matchLabels: + app: catalog-operator + template: + metadata: + labels: + app: catalog-operator + spec: + serviceAccountName: olm-operator-serviceaccount + containers: + - name: catalog-operator + command: + - /bin/catalog + args: + - '--namespace' + - {{ .Values.olmNamespace }} + - --configmapServerImage={{ if .Values.registry }}{{ .Values.registry }}/quay.io-operator-framework-{{ else }}quay.io/operator-framework/{{ end }}configmap-operator-registry:latest + - --util-image + - {{ if .Values.registry }}{{ .Values.registry }}/quay.io-operator-framework-{{ else }}quay.io/operator-framework/{{ end }}olm@sha256:1b6002156f568d722c29138575733591037c24b4bfabc67946f268ce4752c3e6 + - --opmImage + - {{ if .Values.registry }}{{ .Values.registry }}/quay.io-operator-framework-{{ else }}quay.io/operator-framework/{{ end }}opm@sha256:d999588bd4e9509ec9e75e49adfb6582d256e9421e454c7fb5e9fe57e7b1aada + image: {{ if .Values.registry }}{{ .Values.registry }}/quay.io-operator-framework-{{ else }}quay.io/operator-framework/{{ end }}olm@sha256:1b6002156f568d722c29138575733591037c24b4bfabc67946f268ce4752c3e6 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 8080 + - containerPort: 8081 + name: metrics + protocol: TCP + livenessProbe: + httpGet: + path: /healthz + port: 8080 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + terminationMessagePolicy: FallbackToLogsOnError + env: + resources: + requests: + cpu: 10m + memory: 80Mi + nodeSelector: + kubernetes.io/os: linux + tolerations: + - key: "kubernetes.io/arch" + operator: "Equal" + value: "amd64" + effect: "NoSchedule" + - key: "kubernetes.io/arch" + operator: "Equal" + value: "amd64" + effect: "NoExecute" + - key: "kubernetes.io/arch" + operator: "Equal" + value: "arm64" + effect: "NoSchedule" + - key: "kubernetes.io/arch" + operator: "Equal" + value: "arm64" + effect: "NoExecute" +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: aggregate-olm-edit + labels: + rbac.authorization.k8s.io/aggregate-to-admin: "true" + rbac.authorization.k8s.io/aggregate-to-edit: "true" +rules: +- apiGroups: ["operators.coreos.com"] + resources: ["subscriptions"] + verbs: ["create", "update", "patch", "delete"] +- apiGroups: ["operators.coreos.com"] + resources: ["clusterserviceversions", "catalogsources", "installplans", "subscriptions"] + verbs: ["delete"] +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: aggregate-olm-view + labels: + rbac.authorization.k8s.io/aggregate-to-admin: "true" + rbac.authorization.k8s.io/aggregate-to-edit: "true" + rbac.authorization.k8s.io/aggregate-to-view: "true" +rules: +- apiGroups: ["operators.coreos.com"] + resources: ["clusterserviceversions", "catalogsources", "installplans", "subscriptions", "operatorgroups"] + verbs: ["get", "list", "watch"] +- apiGroups: ["packages.operators.coreos.com"] + resources: ["packagemanifests", "packagemanifests/icon"] + verbs: ["get", "list", "watch"] +--- +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: olm-operators + namespace: {{ .Values.olmNamespace }} +spec: + targetNamespaces: + - {{ .Values.olmNamespace }} +{{- end}} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/templates/01_px_olm.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/templates/01_px_olm.yaml new file mode 100644 index 0000000000..2c2921958e --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/templates/01_px_olm.yaml @@ -0,0 +1,13 @@ +{{ if not (eq .Values.olmOperatorNamespace .Release.Namespace) }} +--- +apiVersion: v1 +kind: Namespace +metadata: + name: {{ .Values.olmOperatorNamespace }} +{{ end }} +--- +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: global-operators + namespace: {{ .Values.olmOperatorNamespace }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/templates/02_catalog.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/templates/02_catalog.yaml new file mode 100644 index 0000000000..e7f68804a0 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/templates/02_catalog.yaml @@ -0,0 +1,37 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: CatalogSource +metadata: + name: pixie-operator-index + namespace: {{ .Values.olmOperatorNamespace }} + {{- if .Values.olmCatalogSource.annotations }} + annotations: {{ .Values.olmCatalogSource.annotations | toYaml | nindent 4 }} + {{- end }} + {{- if .Values.olmCatalogSource.labels }} + labels: {{ .Values.olmCatalogSource.labels | toYaml | nindent 4 }} + {{- end }} +spec: + sourceType: grpc + image: {{ if .Values.registry }}{{ .Values.registry }}/gcr.io-pixie-oss-pixie-prod-operator-bundle_index:0.0.1{{ else }}gcr.io/pixie-oss/pixie-prod/operator/bundle_index:0.0.1{{ end }} + displayName: Pixie Vizier Operator + publisher: px.dev + updateStrategy: + registryPoll: + interval: 10m + grpcPodConfig: + tolerations: + - key: "kubernetes.io/arch" + operator: "Equal" + value: "amd64" + effect: "NoSchedule" + - key: "kubernetes.io/arch" + operator: "Equal" + value: "amd64" + effect: "NoExecute" + - key: "kubernetes.io/arch" + operator: "Equal" + value: "arm64" + effect: "NoSchedule" + - key: "kubernetes.io/arch" + operator: "Equal" + value: "arm64" + effect: "NoExecute" diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/templates/03_subscription.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/templates/03_subscription.yaml new file mode 100644 index 0000000000..78223cc9e3 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/templates/03_subscription.yaml @@ -0,0 +1,11 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: pixie-operator-subscription + namespace: {{ .Values.olmOperatorNamespace }} +spec: + channel: {{ .Values.olmBundleChannel }} + name: pixie-operator + source: pixie-operator-index + sourceNamespace: {{ .Values.olmOperatorNamespace }} + installPlanApproval: Automatic diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/templates/04_vizier.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/templates/04_vizier.yaml new file mode 100644 index 0000000000..7c8ca65ad2 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/templates/04_vizier.yaml @@ -0,0 +1,100 @@ +apiVersion: px.dev/v1alpha1 +kind: Vizier +metadata: + name: {{ .Values.name }} + namespace: {{ .Release.Namespace }} +spec: + {{- if .Values.version }} + version: {{ .Values.version }} + {{- end }} + {{- if .Values.deployKey }} + deployKey: {{ .Values.deployKey }} + {{- end }} + {{- if .Values.customDeployKeySecret }} + customDeployKeySecret: {{ .Values.customDeployKeySecret }} + {{- end }} + cloudAddr: {{ .Values.cloudAddr }} + disableAutoUpdate: {{ .Values.disableAutoUpdate }} + useEtcdOperator: {{ .Values.useEtcdOperator }} + {{- if (.Values.global).cluster }} + clusterName: {{ .Values.global.cluster }} + {{- else if .Values.clusterName }} + clusterName: {{ .Values.clusterName }} + {{- end }} + {{- if .Values.devCloudNamespace }} + devCloudNamespace: {{ .Values.devCloudNamespace }} + {{- end }} + {{- if .Values.pemMemoryLimit }} + pemMemoryLimit: {{ .Values.pemMemoryLimit }} + {{- end }} + {{- if .Values.pemMemoryRequest }} + pemMemoryRequest: {{ .Values.pemMemoryRequest }} + {{- end }} + {{- if .Values.dataAccess }} + dataAccess: {{ .Values.dataAccess }} + {{- end }} + {{- if .Values.patches }} + patches: {{ .Values.patches | toYaml | nindent 4 }} + {{- end }} + {{- if ((.Values.global).images).registry }} + registry: {{ .Values.global.images.registry }} + {{- else if .Values.registry }} + registry: {{ .Values.registry }} + {{- end}} + {{- if .Values.autopilot }} + autopilot: {{ .Values.autopilot }} + {{- end}} + {{- if .Values.dataCollectorParams }} + dataCollectorParams: + {{- if .Values.dataCollectorParams.datastreamBufferSize }} + datastreamBufferSize: {{ .Values.dataCollectorParams.datastreamBufferSize }} + {{- end }} + {{- if .Values.dataCollectorParams.datastreamBufferSpikeSize }} + datastreamBufferSpikeSize: {{ .Values.dataCollectorParams.datastreamBufferSpikeSize }} + {{- end }} + {{- if .Values.dataCollectorParams.customPEMFlags }} + customPEMFlags: + {{- range $key, $value := .Values.dataCollectorParams.customPEMFlags}} + {{$key}}: "{{$value}}" + {{- end}} + {{- end }} + {{- end}} + {{- if .Values.leadershipElectionParams }} + leadershipElectionParams: + {{- if .Values.leadershipElectionParams.electionPeriodMs }} + electionPeriodMs: {{ .Values.leadershipElectionParams.electionPeriodMs }} + {{- end }} + {{- end }} + {{- if or .Values.pod.securityContext (or .Values.pod.nodeSelector (or .Values.pod.tolerations (or .Values.pod.annotations (or .Values.pod.labels .Values.pod.resources)))) }} + pod: + {{- if .Values.pod.annotations }} + annotations: {{ .Values.pod.annotations | toYaml | nindent 6 }} + {{- end }} + {{- if .Values.pod.labels }} + labels: {{ .Values.pod.labels | toYaml | nindent 6 }} + {{- end }} + {{- if .Values.pod.resources }} + resources: {{ .Values.pod.resources | toYaml | nindent 6 }} + {{- end }} + {{- if .Values.pod.nodeSelector }} + nodeSelector: {{ .Values.pod.nodeSelector | toYaml | nindent 6 }} + {{- end }} + {{- if .Values.pod.tolerations }} + tolerations: {{ .Values.pod.tolerations | toYaml | nindent 4 }} + {{- end }} + {{- if .Values.pod.securityContext }} + securityContext: + enabled: {{ .Values.pod.securityContext.enabled }} + {{- if .Values.pod.securityContext.enabled }} + {{- if .Values.pod.securityContext.fsGroup }} + fsGroup: {{ .Values.pod.securityContext.fsGroup }} + {{- end }} + {{- if .Values.pod.securityContext.runAsUser }} + runAsUser: {{ .Values.pod.securityContext.runAsUser }} + {{- end }} + {{- if .Values.pod.securityContext.runAsGroup }} + runAsGroup: {{ .Values.pod.securityContext.runAsGroup }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/templates/deleter.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/templates/deleter.yaml new file mode 100644 index 0000000000..b1cde0c927 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/templates/deleter.yaml @@ -0,0 +1,25 @@ +apiVersion: batch/v1 +kind: Job +metadata: + annotations: + helm.sh/hook: pre-delete + helm.sh/hook-delete-policy: hook-succeeded + name: vizier-deleter + namespace: '{{ .Release.Namespace }}' +spec: + template: + metadata: + name: vizier-deleter + spec: + containers: + - env: + - name: PL_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: PL_VIZIER_NAME + value: '{{ .Values.name }}' + image: gcr.io/pixie-oss/pixie-prod/operator-vizier_deleter:0.1.6 + name: delete-job + restartPolicy: Never + serviceAccountName: pl-deleter-service-account diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/templates/deleter_role.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/templates/deleter_role.yaml new file mode 100644 index 0000000000..73e5ec7e48 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/templates/deleter_role.yaml @@ -0,0 +1,77 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: pl-deleter-service-account +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: pl-deleter-cluster-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: pl-deleter-role +subjects: +- kind: ServiceAccount + name: pl-deleter-service-account + namespace: "{{ .Release.Namespace }}" +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: pl-deleter-cluster-role +rules: +# Allow actions on Kubernetes objects +- apiGroups: + - rbac.authorization.k8s.io + - etcd.database.coreos.com + - nats.io + resources: + - clusterroles + - clusterrolebindings + - persistentvolumes + - etcdclusters + - natsclusters + verbs: ["*"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: pl-deleter-role +rules: +- apiGroups: + - "" + - apps + - rbac.authorization.k8s.io + - extensions + - batch + - policy + resources: + - configmaps + - secrets + - pods + - services + - deployments + - daemonsets + - persistentvolumes + - roles + - rolebindings + - serviceaccounts + - statefulsets + - cronjobs + - jobs + verbs: ["*"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: pl-deleter-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: pl-deleter-role +subjects: +- kind: ServiceAccount + name: pl-deleter-service-account + namespace: "{{ .Release.Namespace }}" diff --git a/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/values.yaml b/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/values.yaml new file mode 100644 index 0000000000..a3ffe7c9d3 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/charts/pixie-operator-chart/values.yaml @@ -0,0 +1,75 @@ +## OLM configuration +# OLM is used for deploying and ensuring the operator is up-to-date. +# deployOLM indicates whether OLM should be deployed. This should only be +# disabled if an instance of OLM is already configured on the cluster. +# Should be string "true" if true, but "false" otherwise. If empty, defaults +# to whether OLM is present in the cluster. +deployOLM: "" +# The namespace that olm should run in. If olm has already been deployed +# to the cluster, this should be the namespace that olm is already running in. +olmNamespace: "olm" +# The namespace which olm operators should run in. If olm has already +# been deployed to the cluster, this should be the namespace that the olm operators +# are running in. +olmOperatorNamespace: "px-operator" +# The bundle channel which OLM should listen to for the Vizier operator bundles. +# Should be "stable" for production-versions of the operator, and "test" for release candidates. +olmBundleChannel: "stable" +# Optional annotations and labels for CatalogSource. +olmCatalogSource: + # Optional custom annotations to add to deployed pods managed by CatalogSource object. + annotations: {} + # Optional custom labels to add to deployed pods managed by CatalogSource object. + labels: {} +## Vizier configuration +# The name of the Vizier instance deployed to the cluster. +name: "pixie" +# The name of the cluster that the Vizier is monitoring. If empty, +# a random name will be generated. +clusterName: "" +# The version of the Vizier instance deployed to the cluster. If empty, +# the operator will automatically deploy the latest version. +version: "" +# The deploy key is used to link the deployed Vizier to a specific user/project. +# This is required if not specifying a customDeployKeySecret, and can be generated through the UI or CLI. +deployKey: "" +# The deploy key may be read from a custom secret in the Pixie namespace. This secret should be formatted where the +# key of the deploy key is "deploy-key". +customDeployKeySecret: "" +# Whether auto-update should be disabled. +disableAutoUpdate: false +# Whether the metadata service should use etcd for in-memory storage. Recommended +# only for clusters which do not have persistent volumes configured. +useEtcdOperator: false +# The address of the Pixie cloud instance that the Vizier should be connected to. +# This should only be updated when using a self-hosted version of Pixie Cloud. +cloudAddr: "withpixie.ai:443" +# DevCloudNamespace should be specified only for self-hosted versions of Pixie cloud which have no ingress to help +# redirect traffic to the correct service. The DevCloudNamespace is the namespace that the dev Pixie cloud is +# running on, for example: "plc-dev". +devCloudNamespace: "" +# A memory limit applied specifically to PEM pods. If none is specified, a default limit of 2Gi is set. +pemMemoryLimit: "" +# A memory request applied specifically to PEM pods. If none is specified, it will default to pemMemoryLimit. +pemMemoryRequest: "" +# DataAccess defines the level of data that may be accesssed when executing a script on the cluster. +dataAccess: "Full" +pod: + # Optional custom annotations to add to deployed pods. + annotations: {} + # Optional custom labels to add to deployed pods. + labels: {} + resources: {} + # limits: + # cpu: 500m + # memory: 7Gi + # requests: + # cpu: 100m + # memory: 5Gi + nodeSelector: {} + tolerations: [] +# A set of custom patches to apply to the deployed Vizier resources. +# The key should be the name of the resource to apply the patch to, and the value is the patch to apply. +# Currently, only a JSON format is accepted, such as: +# `{"spec": {"template": {"spec": { "tolerations": [{"key": "test", "operator": "Exists", "effect": "NoExecute" }]}}}}` +patches: {} diff --git a/charts/new-relic/nri-bundle/5.0.91/ci/test-values.yaml b/charts/new-relic/nri-bundle/5.0.91/ci/test-values.yaml new file mode 100644 index 0000000000..7ba6c8c329 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/ci/test-values.yaml @@ -0,0 +1,21 @@ +global: + licenseKey: 1234567890abcdef1234567890abcdef12345678 + cluster: test-cluster + +infrastructure: + enabled: true + +prometheus: + enabled: true + +webhook: + enabled: true + +ksm: + enabled: true + +kubeEvents: + enabled: true + +logging: + enabled: true diff --git a/charts/new-relic/nri-bundle/5.0.91/questions.yaml b/charts/new-relic/nri-bundle/5.0.91/questions.yaml new file mode 100644 index 0000000000..de3fa9fea2 --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/questions.yaml @@ -0,0 +1,113 @@ +questions: +- variable: infrastructure.enabled + default: true + required: false + type: boolean + label: Enable Infrastructure + group: "Select Components" +- variable: prometheus.enabled + default: false + required: false + type: boolean + label: Enable Prometheus + group: "Select Components" +- variable: ksm.enabled + default: false + required: false + type: boolean + label: Enable KSM + group: "Select Components" + description: "This is mandatory if `Enable Infrastructure` is set to `true` and the user does not provide its own instance of KSM version >=1.8 and <=2.0" +- variable: webhook.enabled + default: true + required: false + type: boolean + label: Enable webhook + group: "Select Components" +- variable: kubeEvents.enabled + default: false + required: false + type: boolean + label: Enable Kube Events + group: "Select Components" +- variable: logging.enabled + default: false + required: false + type: boolean + label: Enable Logging + group: "Select Components" +- variable: newrelic-pixie.enabled + default: false + required: false + type: boolean + label: Enable New Relic Pixie Integration + group: "Select Components" + show_subquestion_if: true + subquestions: + - variable: newrelic-pixie.apiKey + default: "" + required: false + type: string + label: New Relic Pixie API Key + group: "Select Components" + description: "Required if deploying Pixie." +- variable: pixie-chart.enabled + default: false + required: false + type: boolean + label: Enable Pixie Chart + group: "Select Components" + show_subquestion_if: true + subquestions: + - variable: pixie-chart.deployKey + default: "" + required: false + type: string + label: Pixie Deploy Key + group: "Select Components" + description: "Required if deploying Pixie." + - variable: pixie-chart.clusterName + default: "" + required: false + type: string + label: Kubernetes Cluster Name for Pixie + group: "Select Components" + description: "Required if deploying Pixie." +- variable: newrelic-infra-operator.enabled + default: false + required: false + type: boolean + label: Enable New Relic Infra Operator + group: "Select Components" +- variable: metrics-adapter.enabled + default: false + required: false + type: boolean + label: Enable Metrics Adapter + group: "Select Components" +- variable: global.licenseKey + default: "xxxx" + required: true + type: string + label: New Relic License Key + group: "Global Settings" +- variable: global.cluster + default: "xxxx" + required: true + type: string + label: Name of Kubernetes Cluster for New Relic + group: "Global Settings" +- variable: global.lowDataMode + default: false + required: false + type: boolean + label: Enable Low Data Mode + description: "Reduces amount of data ingest by New Relic." + group: "Global Settings" +- variable: global.privileged + default: false + required: false + type: boolean + label: Enable Privileged Mode + description: "Allows for access to underlying node from container." + group: "Global Settings" diff --git a/charts/new-relic/nri-bundle/5.0.91/values.yaml b/charts/new-relic/nri-bundle/5.0.91/values.yaml new file mode 100644 index 0000000000..47c58df8ec --- /dev/null +++ b/charts/new-relic/nri-bundle/5.0.91/values.yaml @@ -0,0 +1,169 @@ +newrelic-infrastructure: + # newrelic-infrastructure.enabled -- Install the [`newrelic-infrastructure` chart](https://github.com/newrelic/nri-kubernetes/tree/main/charts/newrelic-infrastructure) + enabled: true + +nri-prometheus: + # nri-prometheus.enabled -- Install the [`nri-prometheus` chart](https://github.com/newrelic/nri-prometheus/tree/main/charts/nri-prometheus) + enabled: false + +nri-metadata-injection: + # nri-metadata-injection.enabled -- Install the [`nri-metadata-injection` chart](https://github.com/newrelic/k8s-metadata-injection/tree/main/charts/nri-metadata-injection) + enabled: true + +kube-state-metrics: + # kube-state-metrics.enabled -- Install the [`kube-state-metrics` chart](https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-state-metrics) from the stable helm charts repository. + # This is mandatory if `infrastructure.enabled` is set to `true` and the user does not provide its own instance of KSM version >=1.8 and <=2.0. Note, kube-state-metrics v2+ disables labels/annotations + # metrics by default. You can enable the target labels/annotations metrics to be monitored by using the metricLabelsAllowlist/metricAnnotationsAllowList options described [here](https://github.com/prometheus-community/helm-charts/blob/159cd8e4fb89b8b107dcc100287504bb91bf30e0/charts/kube-state-metrics/values.yaml#L274) in + # your Kubernetes clusters. + enabled: false + +nri-kube-events: + # nri-kube-events.enabled -- Install the [`nri-kube-events` chart](https://github.com/newrelic/nri-kube-events/tree/main/charts/nri-kube-events) + enabled: false + +newrelic-logging: + # newrelic-logging.enabled -- Install the [`newrelic-logging` chart](https://github.com/newrelic/helm-charts/tree/master/charts/newrelic-logging) + enabled: false + +newrelic-pixie: + # newrelic-pixie.enabled -- Install the [`newrelic-pixie`](https://github.com/newrelic/helm-charts/tree/master/charts/newrelic-pixie) + enabled: false + +k8s-agents-operator: + # k8s-agents-operator.enabled -- Install the [`k8s-agents-operator` chart](https://github.com/newrelic/k8s-agents-operator/tree/main/charts/k8s-agents-operator) + enabled: false + +pixie-chart: + # pixie-chart.enabled -- Install the [`pixie-chart` chart](https://docs.pixielabs.ai/installing-pixie/install-schemes/helm/#3.-deploy) + enabled: false + +newrelic-infra-operator: + # newrelic-infra-operator.enabled -- Install the [`newrelic-infra-operator` chart](https://github.com/newrelic/newrelic-infra-operator/tree/main/charts/newrelic-infra-operator) (Beta) + enabled: false + +newrelic-prometheus-agent: + # newrelic-prometheus-agent.enabled -- Install the [`newrelic-prometheus-agent` chart](https://github.com/newrelic/newrelic-prometheus-configurator/tree/main/charts/newrelic-prometheus-agent) + enabled: false + +newrelic-k8s-metrics-adapter: + # newrelic-k8s-metrics-adapter.enabled -- Install the [`newrelic-k8s-metrics-adapter.` chart](https://github.com/newrelic/newrelic-k8s-metrics-adapter/tree/main/charts/newrelic-k8s-metrics-adapter) (Beta) + enabled: false + + +# -- change the behaviour globally to all the supported helm charts. +# See [user's guide of the common library](https://github.com/newrelic/helm-charts/blob/master/library/common-library/README.md) for further information. +# @default -- See [`values.yaml`](values.yaml) +global: + # -- The cluster name for the Kubernetes cluster. + cluster: "" + + # -- The license key for your New Relic Account. This will be preferred configuration option if both `licenseKey` and `customSecret` are specified. + licenseKey: "" + # -- The license key for your New Relic Account. This will be preferred configuration option if both `insightsKey` and `customSecret` are specified. + insightsKey: "" + # -- Name of the Secret object where the license key is stored + customSecretName: "" + # -- Key in the Secret object where the license key is stored + customSecretLicenseKey: "" + + # -- Additional labels for chart objects + labels: {} + # -- Additional labels for chart pods + podLabels: {} + + images: + # -- Changes the registry where to get the images. Useful when there is an internal image cache/proxy + registry: "" + # -- Set secrets to be able to fetch images + pullSecrets: [] + + serviceAccount: + # -- Add these annotations to the service account we create + annotations: {} + # -- Configures if the service account should be created or not + create: + # -- Change the name of the service account. This is honored if you disable on this chart the creation of the service account so you can use your own + name: + + # -- (bool) Sets pod's hostNetwork + # @default -- false + hostNetwork: + # -- Sets pod's dnsConfig + dnsConfig: {} + + # -- Sets pod's priorityClassName + priorityClassName: "" + # -- Sets security context (at pod level) + podSecurityContext: {} + # -- Sets security context (at container level) + containerSecurityContext: {} + + # -- Sets pod/node affinities + affinity: {} + # -- Sets pod's node selector + nodeSelector: {} + # -- Sets pod's tolerations to node taints + tolerations: [] + + # -- Adds extra attributes to the cluster and all the metrics emitted to the backend + customAttributes: {} + + # -- (bool) Reduces number of metrics sent in order to reduce costs + # @default -- false + lowDataMode: + + # -- (bool) In each integration it has different behavior. See [Further information](#values-managed-globally-3) but all aims to send less metrics to the backend to try to save costs | + # @default -- false + privileged: + + # -- (bool) Must be set to `true` when deploying in an EKS Fargate environment + # @default -- false + fargate: + + # -- Configures the integration to send all HTTP/HTTPS request through the proxy in that URL. The URL should have a standard format like `https://user:password@hostname:port` + proxy: "" + + # -- (bool) Send the metrics to the staging backend. Requires a valid staging license key + # @default -- false + nrStaging: + fedramp: + # fedramp.enabled -- (bool) Enables FedRAMP + # @default -- false + enabled: + + # -- (bool) Sets the debug logs to this integration or all integrations if it is set globally + # @default -- false + verboseLog: + + +# To add values to the subcharts. Follow Helm's guide: https://helm.sh/docs/chart_template_guide/subcharts_and_globals + +# If you wish to monitor services running on Kubernetes you can provide integrations +# configuration under `integrations_config` that it will passed down to the `newrelic-infrastructure` chart. +# +# You just need to create a new entry where the "name" is the filename of the configuration file and the data is the content of +# the integration configuration. The name must end in ".yaml" as this will be the +# filename generated and the Infrastructure agent only looks for YAML files. +# +# The data part is the actual integration configuration as described in the spec here: +# https://docs.newrelic.com/docs/integrations/integrations-sdk/file-specifications/integration-configuration-file-specifications-agent-v180 +# +# In the following example you can see how to monitor a Redis integration with autodiscovery +# +# +# newrelic-infrastructure: +# integrations: +# nri-redis-sampleapp: +# discovery: +# command: +# exec: /var/db/newrelic-infra/nri-discovery-kubernetes --tls --port 10250 +# match: +# label.app: sampleapp +# integrations: +# - name: nri-redis +# env: +# # using the discovered IP as the hostname address +# HOSTNAME: ${discovery.ip} +# PORT: 6379 +# labels: +# env: test diff --git a/charts/speedscale/speedscale-operator/2.2.342/.helmignore b/charts/speedscale/speedscale-operator/2.2.342/.helmignore new file mode 100644 index 0000000000..0e8a0eb36f --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.342/.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 +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/speedscale/speedscale-operator/2.2.342/Chart.yaml b/charts/speedscale/speedscale-operator/2.2.342/Chart.yaml new file mode 100644 index 0000000000..56dd9da4cd --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.342/Chart.yaml @@ -0,0 +1,27 @@ +annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Speedscale Operator + catalog.cattle.io/kube-version: '>= 1.17.0-0' + catalog.cattle.io/release-name: speedscale-operator +apiVersion: v1 +appVersion: 2.2.342 +description: Stress test your APIs with real world scenarios. Collect and replay + traffic without scripting. +home: https://speedscale.com +icon: file://assets/icons/speedscale-operator.png +keywords: +- speedscale +- test +- testing +- regression +- reliability +- load +- replay +- network +- traffic +kubeVersion: '>= 1.17.0-0' +maintainers: +- email: support@speedscale.com + name: Speedscale Support +name: speedscale-operator +version: 2.2.342 diff --git a/charts/speedscale/speedscale-operator/2.2.342/LICENSE b/charts/speedscale/speedscale-operator/2.2.342/LICENSE new file mode 100644 index 0000000000..b78723d62f --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.342/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2021 Speedscale + + 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/speedscale/speedscale-operator/2.2.342/README.md b/charts/speedscale/speedscale-operator/2.2.342/README.md new file mode 100644 index 0000000000..6ca25eed9d --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.342/README.md @@ -0,0 +1,111 @@ +![GitHub Tag](https://img.shields.io/github/v/tag/speedscale/operator-helm) + + +# Speedscale Operator + +The [Speedscale](https://www.speedscale.com) Operator is a [Kubernetes operator](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/) +that watches for deployments to be applied to the cluster and takes action based on annotations. The operator +can inject a proxy to capture traffic into or out of applications, or setup an isolation test environment around +a deployment for testing. The operator itself is a deployment that will be always present on the cluster once +the helm chart is installed. + +## Prerequisites + +- Kubernetes 1.20+ +- Helm 3+ +- Appropriate [network and firewall configuration](https://docs.speedscale.com/reference/networking) for Speedscale cloud and webhook traffic + +## Get Repo Info + +```bash +helm repo add speedscale https://speedscale.github.io/operator-helm/ +helm repo update +``` + +_See [helm repo](https://helm.sh/docs/helm/helm_repo/) for command documentation._ + +## Install Chart + +An API key is required. Sign up for a [free Speedscale trial](https://speedscale.com/free-trial/) if you do not have one. + +```bash +helm install speedscale-operator speedscale/speedscale-operator \ + -n speedscale \ + --create-namespace \ + --set apiKey= \ + --set clusterName= +``` + +_See [helm install](https://helm.sh/docs/helm/helm_install/) for command documentation._ + +### Pre-install job failure + +We use pre-install job to check provided API key and provision some of the required resources. + +If the job failed during the installation, you'll see the following error during install: + +``` +Error: INSTALLATION FAILED: failed pre-install: job failed: BackoffLimitExceeded +``` + +You can inspect the logs using this command: + +```bash +kubectl -n speedscale logs job/speedscale-operator-pre-install +``` + +After fixing the error, uninstall the helm release, delete the failed job +and try installing again: + +```bash +helm -n speedscale uninstall speedscale-operator +kubectl -n speedscale delete job speedscale-operator-pre-install +``` + +## Uninstall Chart + +```bash +helm -n speedscale uninstall speedscale-operator +``` + +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._ + +CRDs created by this chart are not removed by default and should be manually cleaned up: + +```bash +kubectl delete crd trafficreplays.speedscale.com +``` + +## Upgrading Chart + +```bash +helm repo update +helm -n speedscale upgrade speedscale-operator speedscale/speedscale-operator +``` + +Resources capturing traffic will need to be rolled to pick up the latest +Speedscale sidecar. Use the rollout restart command for each namespace and +resource type: + +```bash +kubectl -n rollout restart deployment +``` + +With Helm v3, CRDs created by this chart are not updated by default +and should be manually updated. +Consult also the [Helm Documentation on CRDs](https://helm.sh/docs/chart_best_practices/custom_resource_definitions). + +_See [helm upgrade](https://helm.sh/docs/helm/helm_upgrade/) for command documentation._ + +### Upgrading an existing Release to a new version + +A major chart version change (like v1.2.3 -> v2.0.0) indicates that there is an +incompatible breaking change needing manual actions. + + +## Help + +Speedscale docs information available at [docs.speedscale.com](https://docs.speedscale.com) or join us +on the [Speedscale community Slack](https://join.slack.com/t/speedscalecommunity/shared_invite/zt-x5rcrzn4-XHG1QqcHNXIM~4yozRrz8A)! diff --git a/charts/speedscale/speedscale-operator/2.2.342/app-readme.md b/charts/speedscale/speedscale-operator/2.2.342/app-readme.md new file mode 100644 index 0000000000..6ca25eed9d --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.342/app-readme.md @@ -0,0 +1,111 @@ +![GitHub Tag](https://img.shields.io/github/v/tag/speedscale/operator-helm) + + +# Speedscale Operator + +The [Speedscale](https://www.speedscale.com) Operator is a [Kubernetes operator](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/) +that watches for deployments to be applied to the cluster and takes action based on annotations. The operator +can inject a proxy to capture traffic into or out of applications, or setup an isolation test environment around +a deployment for testing. The operator itself is a deployment that will be always present on the cluster once +the helm chart is installed. + +## Prerequisites + +- Kubernetes 1.20+ +- Helm 3+ +- Appropriate [network and firewall configuration](https://docs.speedscale.com/reference/networking) for Speedscale cloud and webhook traffic + +## Get Repo Info + +```bash +helm repo add speedscale https://speedscale.github.io/operator-helm/ +helm repo update +``` + +_See [helm repo](https://helm.sh/docs/helm/helm_repo/) for command documentation._ + +## Install Chart + +An API key is required. Sign up for a [free Speedscale trial](https://speedscale.com/free-trial/) if you do not have one. + +```bash +helm install speedscale-operator speedscale/speedscale-operator \ + -n speedscale \ + --create-namespace \ + --set apiKey= \ + --set clusterName= +``` + +_See [helm install](https://helm.sh/docs/helm/helm_install/) for command documentation._ + +### Pre-install job failure + +We use pre-install job to check provided API key and provision some of the required resources. + +If the job failed during the installation, you'll see the following error during install: + +``` +Error: INSTALLATION FAILED: failed pre-install: job failed: BackoffLimitExceeded +``` + +You can inspect the logs using this command: + +```bash +kubectl -n speedscale logs job/speedscale-operator-pre-install +``` + +After fixing the error, uninstall the helm release, delete the failed job +and try installing again: + +```bash +helm -n speedscale uninstall speedscale-operator +kubectl -n speedscale delete job speedscale-operator-pre-install +``` + +## Uninstall Chart + +```bash +helm -n speedscale uninstall speedscale-operator +``` + +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._ + +CRDs created by this chart are not removed by default and should be manually cleaned up: + +```bash +kubectl delete crd trafficreplays.speedscale.com +``` + +## Upgrading Chart + +```bash +helm repo update +helm -n speedscale upgrade speedscale-operator speedscale/speedscale-operator +``` + +Resources capturing traffic will need to be rolled to pick up the latest +Speedscale sidecar. Use the rollout restart command for each namespace and +resource type: + +```bash +kubectl -n rollout restart deployment +``` + +With Helm v3, CRDs created by this chart are not updated by default +and should be manually updated. +Consult also the [Helm Documentation on CRDs](https://helm.sh/docs/chart_best_practices/custom_resource_definitions). + +_See [helm upgrade](https://helm.sh/docs/helm/helm_upgrade/) for command documentation._ + +### Upgrading an existing Release to a new version + +A major chart version change (like v1.2.3 -> v2.0.0) indicates that there is an +incompatible breaking change needing manual actions. + + +## Help + +Speedscale docs information available at [docs.speedscale.com](https://docs.speedscale.com) or join us +on the [Speedscale community Slack](https://join.slack.com/t/speedscalecommunity/shared_invite/zt-x5rcrzn4-XHG1QqcHNXIM~4yozRrz8A)! diff --git a/charts/speedscale/speedscale-operator/2.2.342/questions.yaml b/charts/speedscale/speedscale-operator/2.2.342/questions.yaml new file mode 100644 index 0000000000..29aee38958 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.342/questions.yaml @@ -0,0 +1,9 @@ +questions: +- variable: apiKey + default: "fffffffffffffffffffffffffffffffffffffffffffff" + description: "An API key is required to connect to the Speedscale cloud." + required: true + type: string + label: API Key + group: Authentication + diff --git a/charts/speedscale/speedscale-operator/2.2.342/templates/NOTES.txt b/charts/speedscale/speedscale-operator/2.2.342/templates/NOTES.txt new file mode 100644 index 0000000000..cabb59b175 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.342/templates/NOTES.txt @@ -0,0 +1,12 @@ +Thank you for installing the Speedscale Operator! + +Next you'll need to add the Speedscale Proxy Sidecar to your deployments. +See https://docs.speedscale.com/setup/sidecar/install/ + +If upgrading use the rollout restart command for each namespace and resource +type to ensure Speedscale sidecars are updated: + + kubectl -n rollout restart deployment + +Once your deployment is running the sidecar your service will show up on +https://app.speedscale.com/. diff --git a/charts/speedscale/speedscale-operator/2.2.342/templates/admission.yaml b/charts/speedscale/speedscale-operator/2.2.342/templates/admission.yaml new file mode 100644 index 0000000000..301748a61d --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.342/templates/admission.yaml @@ -0,0 +1,209 @@ +{{- $cacrt := "" -}} +{{- $crt := "" -}} +{{- $key := "" -}} +{{- $s := (lookup "v1" "Secret" .Release.Namespace "speedscale-webhook-certs") -}} +{{- if $s -}} +{{- $cacrt = index $s.data "ca.crt" | default (index $s.data "tls.crt") | b64dec -}} +{{- $crt = index $s.data "tls.crt" | b64dec -}} +{{- $key = index $s.data "tls.key" | b64dec -}} +{{ else }} +{{- $altNames := list ( printf "speedscale-operator.%s" .Release.Namespace ) ( printf "speedscale-operator.%s.svc" .Release.Namespace ) -}} +{{- $ca := genCA "speedscale-operator" 3650 -}} +{{- $cert := genSignedCert "speedscale-operator" nil $altNames 3650 $ca -}} +{{- $cacrt = $ca.Cert -}} +{{- $crt = $cert.Cert -}} +{{- $key = $cert.Key -}} +{{- end -}} +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + creationTimestamp: null + name: speedscale-operator + annotations: + argocd.argoproj.io/hook: PreSync + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + caBundle: {{ $cacrt | b64enc }} + service: + name: speedscale-operator + namespace: {{ .Release.Namespace }} + path: /mutate + failurePolicy: Ignore + name: sidecar.speedscale.com + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: "NotIn" + values: + - kube-system + - kube-node-lease + {{- if .Values.namespaceSelector }} + - key: kubernetes.io/metadata.name + operator: "In" + values: + {{- range .Values.namespaceSelector }} + - {{ . | quote }} + {{- end }} + {{- end }} + reinvocationPolicy: IfNeeded + rules: + - apiGroups: + - apps + - batch + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - deployments + - statefulsets + - daemonsets + - jobs + - replicasets + - apiGroups: + - "" + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - pods + - apiGroups: + - argoproj.io + apiVersions: + - "*" + operations: + - CREATE + - UPDATE + - DELETE + resources: + - rollouts + sideEffects: None + timeoutSeconds: 10 +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + creationTimestamp: null + name: speedscale-operator-replay + annotations: + argocd.argoproj.io/hook: PreSync + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + caBundle: {{ $cacrt | b64enc }} + service: + name: speedscale-operator + namespace: {{ .Release.Namespace }} + path: /mutate-speedscale-com-v1-trafficreplay + failurePolicy: Fail + name: replay.speedscale.com + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: "NotIn" + values: + - kube-system + - kube-node-lease + {{- if .Values.namespaceSelector }} + - key: kubernetes.io/metadata.name + operator: "In" + values: + {{- range .Values.namespaceSelector }} + - {{ . | quote }} + {{- end }} + {{- end }} + rules: + - apiGroups: + - speedscale.com + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - trafficreplays + sideEffects: None + timeoutSeconds: 10 +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + creationTimestamp: null + name: speedscale-operator-replay + annotations: + argocd.argoproj.io/hook: PreSync + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + caBundle: {{ $cacrt | b64enc }} + service: + name: speedscale-operator + namespace: {{ .Release.Namespace }} + path: /validate-speedscale-com-v1-trafficreplay + failurePolicy: Fail + name: replay.speedscale.com + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: "NotIn" + values: + - kube-system + - kube-node-lease + {{- if .Values.namespaceSelector }} + - key: kubernetes.io/metadata.name + operator: "In" + values: + {{- range .Values.namespaceSelector }} + - {{ . | quote }} + {{- end }} + {{- end }} + rules: + - apiGroups: + - speedscale.com + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - trafficreplays + sideEffects: None + timeoutSeconds: 10 +--- +apiVersion: v1 +kind: Secret +metadata: + annotations: + helm.sh/hook: pre-install + helm.sh/hook-delete-policy: before-hook-creation + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} + creationTimestamp: null + name: speedscale-webhook-certs + namespace: {{ .Release.Namespace }} +type: kubernetes.io/tls +data: + ca.crt: {{ $cacrt | b64enc }} + tls.crt: {{ $crt | b64enc }} + tls.key: {{ $key | b64enc }} diff --git a/charts/speedscale/speedscale-operator/2.2.342/templates/configmap.yaml b/charts/speedscale/speedscale-operator/2.2.342/templates/configmap.yaml new file mode 100644 index 0000000000..af735e2886 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.342/templates/configmap.yaml @@ -0,0 +1,41 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: speedscale-operator + namespace: {{ .Release.Namespace }} + annotations: + argocd.argoproj.io/hook: PreSync + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} +data: + CLUSTER_NAME: {{ .Values.clusterName }} + IMAGE_PULL_POLICY: {{ .Values.image.pullPolicy }} + IMAGE_PULL_SECRETS: "" + IMAGE_REGISTRY: {{ .Values.image.registry }} + IMAGE_TAG: {{ .Values.image.tag }} + INSTANCE_ID: '{{- $cm := (lookup "v1" "ConfigMap" .Release.Namespace "speedscale-operator") -}}{{ if $cm }}{{ $cm.data.INSTANCE_ID }}{{ else }}{{ ( printf "%s-%s" .Values.clusterName uuidv4 ) }}{{ end }}' + LOG_LEVEL: {{ .Values.logLevel }} + SPEEDSCALE_DLP_CONFIG: {{ .Values.dlp.config }} + SPEEDSCALE_FILTER_RULE: {{ .Values.filterRule }} + TELEMETRY_INTERVAL: 1s + WITH_DLP: {{ .Values.dlp.enabled | quote }} + WITH_INSPECTOR: {{ .Values.dashboardAccess | quote }} + API_KEY_SECRET_NAME: {{ .Values.apiKeySecret | quote }} + DEPLOY_DEMO: {{ .Values.deployDemo | quote }} + GLOBAL_ANNOTATIONS: {{ .Values.globalAnnotations | toJson | quote }} + GLOBAL_LABELS: {{ .Values.globalLabels | toJson | quote }} + {{- if .Values.http_proxy }} + HTTP_PROXY: {{ .Values.http_proxy }} + {{- end }} + {{- if .Values.https_proxy }} + HTTPS_PROXY: {{ .Values.https_proxy }} + {{- end }} + {{- if .Values.no_proxy }} + NO_PROXY: {{ .Values.no_proxy }} + {{- end }} + PRIVILEGED_SIDECARS: {{ .Values.privilegedSidecars | quote }} + DISABLE_SMARTDNS: {{ .Values.disableSidecarSmartReverseDNS | quote }} + SIDECAR_CONFIG: {{ .Values.sidecar | toJson | quote }} + FORWARDER_CONFIG: {{ .Values.forwarder | toJson | quote }} diff --git a/charts/speedscale/speedscale-operator/2.2.342/templates/crds/trafficreplays.yaml b/charts/speedscale/speedscale-operator/2.2.342/templates/crds/trafficreplays.yaml new file mode 100644 index 0000000000..5743c5a13c --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.342/templates/crds/trafficreplays.yaml @@ -0,0 +1,515 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + creationTimestamp: null + name: trafficreplays.speedscale.com +spec: + group: speedscale.com + names: + kind: TrafficReplay + listKind: TrafficReplayList + plural: trafficreplays + shortNames: + - replay + singular: trafficreplay + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.active + name: Active + type: boolean + - jsonPath: .spec.mode + name: Mode + type: string + - jsonPath: .status.conditions[-1:].message + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: TrafficReplay is the Schema for the trafficreplays 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: TrafficReplaySpec defines the desired state of TrafficReplay + properties: + buildTag: + description: |- + BuildTag links a unique tag, build hash, etc. to the generated + traffic replay report. That way you can connect the report results to the + version of the code that was tested. + type: string + cleanup: + description: |- + Cleanup is the name of cleanup mode used for this + TrafficReplay. + enum: + - inventory + - all + - none + type: string + collectLogs: + description: |- + CollectLogs enables or disables log collection from target + workload. Defaults to true. + DEPRECATED: use TestReport.ActualConfig.Cluster.CollectLogs + type: boolean + configChecksum: + description: |- + ConfigChecksum, managed my the operator, is the SHA1 checksum of the + configuration. + type: string + customURL: + description: CustomURL allows to specify custom URL to the SUT. + type: string + generatorLowData: + description: |- + GeneratorLowData forces the generator into a high + efficiency/low data output mode. This is ideal for high volume + performance tests. Defaults to false. + DEPRECATED + type: boolean + mode: + description: Mode is the name of replay mode used for this TrafficReplay. + enum: + - full-replay + - responder-only + - generator-only + type: string + needsReport: + description: Indicates whether a responder-only replay needs a report. + type: boolean + proxyMode: + description: |- + ProxyMode defines proxy operational mode used with injected sidecar. + DEPRECATED + type: string + responderLowData: + description: |- + ResponderLowData forces the responder into a high + efficiency/low data output mode. This is ideal for high volume + performance tests. Defaults to false. + DEPRECATED + type: boolean + secretRefs: + description: |- + SecretRefs hold the references to the secrets which contain + various secrets like (e.g. short-lived JWTs to be used by the generator + for authorization with HTTP calls). + items: + description: |- + LocalObjectReference contains enough information to locate the referenced + Kubernetes resource object. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + type: array + sidecar: + description: |- + Sidecar defines sidecar specific configuration. + DEPRECATED: use Workloads + properties: + inject: + description: 'DEPRECATED: do not use' + type: boolean + patch: + description: Patch is .yaml file patch for the Workload + format: byte + type: string + tls: + properties: + in: + description: In provides configuration for sidecar inbound + TLS. + properties: + private: + description: Private is the filename of the TLS inbound + private key. + type: string + public: + description: Public is the filename of the TLS inbound + public key. + type: string + secret: + description: Secret is a secret with the TLS keys to use + for inbound traffic. + type: string + type: object + mutual: + description: Mutual provides configuration for sidecar mutual + TLS. + properties: + private: + description: Private is the filename of the mutual TLS + private key. + type: string + public: + description: Public is the filename of the mutual TLS + public key. + type: string + secret: + description: Secret is a secret with the mutual TLS keys. + type: string + type: object + out: + description: |- + Out enables or disables TLS out on the + sidecar during replay. + type: boolean + type: object + type: object + snapshotID: + description: |- + SnapshotID is the id of the traffic snapshot for this + TrafficReplay. + type: string + testConfigID: + description: |- + TestConfigID is the id of the replay configuration to be used + by the generator and responder for the TrafficReplay. + type: string + timeout: + description: |- + Timeout is the time to wait for replay test to finish. Defaults + to value of the `TIMEOUT` setting of the operator. + type: string + ttlAfterReady: + description: |- + TTLAfterReady provides a TTL (time to live) mechanism to limit + the lifetime of TrafficReplay object that have finished the execution and + reached its final state (either complete or failed). + type: string + workloadRef: + description: |- + WorkloadRef is the reference to the target workload (SUT) for + TrafficReplay. The operations will be performed in the namespace of the + target object. + DEPRECATED: use Workloads + properties: + apiVersion: + description: API version of the referenced object. + type: string + kind: + description: Kind of the referenced object. Defaults to "Deployment". + type: string + name: + description: Name of the referenced object. + type: string + namespace: + description: Namespace of the referenced object. Defaults to the + TrafficReplay namespace. + type: string + required: + - name + type: object + workloads: + description: |- + Workloads define target workloads (SUT) for a TrafficReplay. Many + workloads may be provided, or none. Workloads may be modified and + restarted during replay to configure communication with a responder. + items: + description: |- + Workload represents a Kubernetes workload to be targeted during replay and + associated settings. + properties: + customURI: + description: CustomURI will be target of the traffic instead + of directly targeting workload + type: string + inTrafficKey: + description: 'DEPRECATED: use InTrafficKeys' + type: string + inTrafficKeys: + description: 'DEPRECATED: use Tests' + items: + type: string + type: array + mocks: + description: |- + Mocks are strings used to identify slices of outbound snapshot traffic to + mock for this workload and maps directly to a snapshot's `OutTraffic` + field. Snapshot egress traffic can be split across multiple slices where + each slice contains part of the traffic. A workload may specify multiple + keys and multiple workloads may specify the same key. + + + Only the traffic slices defined here will be mocked. A workload with no + keys defined will not mock any traffic. Pass '*' to mock all traffic. + + + Mock strings may only match part of the snapshot's `OutTraffic` key if the + string matches exactly one key. For example, the test string + `foo.example.com` would match the `OutTraffic` key of + my-service:foo.example.com:8080, as long as no other keys would match + `foo.example.com`. Multiple mocks must be specified for multiple keys + unless using '*'. + items: + type: string + type: array + outTrafficKeys: + description: 'DEPRECATED: use Mocks' + items: + type: string + type: array + ref: + description: |- + Ref is a reference to a cluster workload, like a deployment or a + statefulset. + properties: + apiVersion: + description: API version of the referenced object. + type: string + kind: + description: Kind of the referenced object. Defaults to + "Deployment". + type: string + name: + description: Name of the referenced object. + type: string + namespace: + description: Namespace of the referenced object. Defaults + to the TrafficReplay namespace. + type: string + required: + - name + type: object + routing: + description: Routing configures how workloads route egress traffic + to responders + enum: + - hostalias + - nat + type: string + sidecar: + description: |- + TODO: this is not implemented, come back and replace deprecated Sidecar with workload specific settings + Sidecar defines sidecar specific configuration. + properties: + inject: + description: 'DEPRECATED: do not use' + type: boolean + patch: + description: Patch is .yaml file patch for the Workload + format: byte + type: string + tls: + properties: + in: + description: In provides configuration for sidecar inbound + TLS. + properties: + private: + description: Private is the filename of the TLS + inbound private key. + type: string + public: + description: Public is the filename of the TLS inbound + public key. + type: string + secret: + description: Secret is a secret with the TLS keys + to use for inbound traffic. + type: string + type: object + mutual: + description: Mutual provides configuration for sidecar + mutual TLS. + properties: + private: + description: Private is the filename of the mutual + TLS private key. + type: string + public: + description: Public is the filename of the mutual + TLS public key. + type: string + secret: + description: Secret is a secret with the mutual + TLS keys. + type: string + type: object + out: + description: |- + Out enables or disables TLS out on the + sidecar during replay. + type: boolean + type: object + type: object + tests: + description: |- + Tests are strings used to identify slices of inbound snapshot traffic this + workload is targeting and maps directly to a snapshot's `InTraffic` field. + Snapshot ingress traffic can be split across multiple slices where each + slice contains part of the traffic. A key must only be specified once + across all workloads, but a workload may specify multiple keys. Pass '*' + to match all keys. + + + Test strings may only match part of the snapshot's `InTraffic` key if the + string matches exactly one key. For example, the test string + `foo.example.com` would match the `InTraffic` key of + my-service:foo.example.com:8080, as long as no other keys would match + `foo.example.com` + + + This field is optional in the spec to provide support for single-workload + and legacy replays, but must be specified for multi-workload replays in + order to provide deterministic replay configuration. + items: + type: string + type: array + type: object + type: array + required: + - snapshotID + - testConfigID + type: object + status: + default: + observedGeneration: -1 + description: TrafficReplayStatus defines the observed state of TrafficReplay + properties: + active: + description: Active indicates whether this traffic replay is currently + underway or not. + type: boolean + conditions: + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + finishedTime: + description: Information when the traffic replay has finished. + format: date-time + type: string + initializedTime: + description: Information when the test environment was successfully + prepared. + format: date-time + type: string + lastHeartbeatTime: + description: 'DEPRECATED: will not be set' + format: date-time + type: string + observedGeneration: + description: ObservedGeneration is the last observed generation. + format: int64 + type: integer + reconcileFailures: + description: |- + ReconcileFailures is the number of times the traffic replay controller + experienced an error during the reconciliation process. The traffic + replay will be deleted if too many errors occur. + format: int64 + type: integer + reportID: + description: The id of the traffic replay report created. + type: string + reportURL: + description: The url to the traffic replay report. + type: string + startedTime: + description: Information when the traffic replay has started. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/charts/speedscale/speedscale-operator/2.2.342/templates/deployments.yaml b/charts/speedscale/speedscale-operator/2.2.342/templates/deployments.yaml new file mode 100644 index 0000000000..e5f3292579 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.342/templates/deployments.yaml @@ -0,0 +1,132 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + operator.speedscale.com/ignore: "true" + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} + labels: + app: speedscale-operator + controlplane.speedscale.com/component: operator + {{- if .Values.globalLabels }} +{{ toYaml .Values.globalLabels | indent 4}} + {{- end }} + name: speedscale-operator + namespace: {{ .Release.Namespace }} +spec: + replicas: 1 + selector: + matchLabels: + app: speedscale-operator + controlplane.speedscale.com/component: operator + strategy: + type: Recreate + template: + metadata: + annotations: + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 8}} + {{- end }} + labels: + app: speedscale-operator + controlplane.speedscale.com/component: operator + {{- if .Values.globalLabels }} +{{ toYaml .Values.globalLabels | indent 8}} + {{- end }} + spec: + containers: + - command: + - /operator + env: + - name: POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + envFrom: + - configMapRef: + name: speedscale-operator + # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#container-v1-core + # When a key exists in multiple sources, the value associated with the last source will take precedence. + # Values defined by an Env with a duplicate key will take precedence. + - configMapRef: + name: speedscale-operator-override + optional: true + - secretRef: + name: '{{ ne .Values.apiKeySecret "" | ternary .Values.apiKeySecret "speedscale-apikey" }}' + optional: false + image: '{{ .Values.image.registry }}/operator:{{ .Values.image.tag }}' + imagePullPolicy: {{ .Values.image.pullPolicy }} + livenessProbe: + failureThreshold: 5 + httpGet: + path: /healthz + port: health-check + scheme: HTTP + initialDelaySeconds: 30 + periodSeconds: 30 + successThreshold: 1 + timeoutSeconds: 5 + name: operator + ports: + - containerPort: 443 + name: webhook-server + - containerPort: 8081 + name: health-check + readinessProbe: + failureThreshold: 10 + httpGet: + path: /readyz + port: health-check + scheme: HTTP + initialDelaySeconds: 5 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 5 + resources: {{- toYaml .Values.operator.resources | nindent 10 }} + securityContext: + allowPrivilegeEscalation: false + privileged: false + readOnlyRootFilesystem: true + runAsNonRoot: false + # Run as root to bind 443 https://github.com/kubernetes/kubernetes/issues/56374 + runAsUser: 0 + volumeMounts: + - mountPath: /tmp + name: tmp + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: webhook-certs + readOnly: true + - mountPath: /etc/ssl/speedscale + name: speedscale-tls-out + readOnly: true + hostNetwork: {{ .Values.hostNetwork }} + securityContext: + runAsNonRoot: true + serviceAccountName: speedscale-operator + terminationGracePeriodSeconds: 10 + volumes: + - emptyDir: {} + name: tmp + - name: webhook-certs + secret: + secretName: speedscale-webhook-certs + - name: speedscale-tls-out + secret: + secretName: speedscale-certs + {{- if .Values.affinity }} + affinity: {{ toYaml .Values.affinity | nindent 8 }} + {{- end }} + {{- if .Values.tolerations }} + tolerations: {{ toYaml .Values.tolerations | nindent 8 }} + {{- end }} + {{- if .Values.nodeSelector }} + nodeSelector: {{ toYaml .Values.nodeSelector | nindent 8 }} + {{- end }} diff --git a/charts/speedscale/speedscale-operator/2.2.342/templates/hooks.yaml b/charts/speedscale/speedscale-operator/2.2.342/templates/hooks.yaml new file mode 100644 index 0000000000..3e8231f194 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.342/templates/hooks.yaml @@ -0,0 +1,73 @@ +--- +apiVersion: batch/v1 +kind: Job +metadata: + annotations: + helm.sh/hook: pre-install + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded + helm.sh/hook-weight: "4" + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} + creationTimestamp: null + name: speedscale-operator-pre-install + namespace: {{ .Release.Namespace }} + labels: + {{- if .Values.globalLabels }} +{{ toYaml .Values.globalLabels | indent 4}} + {{- end }} +spec: + backoffLimit: 0 + ttlSecondsAfterFinished: 30 + template: + metadata: + annotations: + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 8}} + {{- end }} + creationTimestamp: null + labels: + {{- if .Values.globalLabels }} +{{ toYaml .Values.globalLabels | indent 8}} + {{- end }} + spec: + containers: + - args: + - |- + # ensure valid settings before the chart reports a successfull install + {{- if .Values.http_proxy }} + HTTP_PROXY={{ .Values.http_proxy | quote }} \ + {{- end }} + {{- if .Values.https_proxy }} + HTTPS_PROXY={{ .Values.https_proxy | quote }} \ + {{- end }} + {{- if .Values.no_proxy }} + NO_PROXY={{ .Values.no_proxy | quote }} \ + {{- end }} + speedctl init --overwrite --no-rcfile-update \ + --api-key $SPEEDSCALE_API_KEY \ + --app-url $SPEEDSCALE_APP_URL + + # in case we're in istio + curl -X POST http://127.0.0.1:15000/quitquitquit || true + command: + - sh + - -c + envFrom: + - secretRef: + name: '{{ ne .Values.apiKeySecret "" | ternary .Values.apiKeySecret "speedscale-apikey" }}' + optional: false + image: '{{ .Values.image.registry }}/speedscale-cli:{{ .Values.image.tag }}' + imagePullPolicy: {{ .Values.image.pullPolicy }} + name: speedscale-cli + resources: {} + restartPolicy: Never + {{- if .Values.affinity }} + affinity: {{ toYaml .Values.affinity | nindent 8 }} + {{- end }} + {{- if .Values.tolerations }} + tolerations: {{ toYaml .Values.tolerations | nindent 8 }} + {{- end }} + {{- if .Values.nodeSelector }} + nodeSelector: {{ toYaml .Values.nodeSelector | nindent 8 }} + {{- end }} diff --git a/charts/speedscale/speedscale-operator/2.2.342/templates/rbac.yaml b/charts/speedscale/speedscale-operator/2.2.342/templates/rbac.yaml new file mode 100644 index 0000000000..e1ea42d999 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.342/templates/rbac.yaml @@ -0,0 +1,244 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: speedscale-operator + {{- if .Values.globalAnnotations }} + annotations: {{ toYaml .Values.globalAnnotations | nindent 4 }} + {{- end }} +rules: +- apiGroups: + - apps + resources: + - deployments + - statefulsets + - daemonsets + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - replicasets + verbs: + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - batch + resources: + - jobs + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - get + - list +- apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + - validatingwebhookconfigurations + verbs: + - get + - list +- apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterrolebindings + - clusterroles + verbs: + - get + - list +- apiGroups: + - "" + resources: + - namespaces + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - configmaps + - secrets + - pods + - services + - serviceaccounts + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - pods/log + verbs: + - get + - list +- apiGroups: + - "" + resources: + - events + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - nodes + verbs: + - get + - list + - watch +- apiGroups: + - metrics.k8s.io + resources: + - pods + verbs: + - get + - list + - watch +- apiGroups: + - rbac.authorization.k8s.io + resources: + - rolebindings + - roles + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - networking.istio.io + resources: + - envoyfilters + - sidecars + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - security.istio.io + resources: + - peerauthentications + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - speedscale.com + resources: + - trafficreplays + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - speedscale.com + resources: + - trafficreplays/status + verbs: + - get + - update + - patch +- apiGroups: + - argoproj.io + resources: + - rollouts + verbs: + - get + - list + - patch + - update + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: speedscale-operator + {{- if .Values.globalAnnotations }} + annotations: {{ toYaml .Values.globalAnnotations | nindent 4 }} + {{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: speedscale-operator +subjects: +- kind: ServiceAccount + name: speedscale-operator + namespace: {{ .Release.Namespace }} +--- +apiVersion: v1 +automountServiceAccountToken: true +kind: ServiceAccount +metadata: + creationTimestamp: null + labels: + app: speedscale-operator + controlplane.speedscale.com/component: operator + name: speedscale-operator + namespace: {{ .Release.Namespace }} + {{- if .Values.globalAnnotations }} + annotations: {{ toYaml .Values.globalAnnotations | nindent 4 }} + {{- end }} diff --git a/charts/speedscale/speedscale-operator/2.2.342/templates/secrets.yaml b/charts/speedscale/speedscale-operator/2.2.342/templates/secrets.yaml new file mode 100644 index 0000000000..1fb6999e4c --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.342/templates/secrets.yaml @@ -0,0 +1,18 @@ +--- +{{ if .Values.apiKey }} +apiVersion: v1 +kind: Secret +metadata: + name: speedscale-apikey + namespace: {{ .Release.Namespace }} + annotations: + helm.sh/hook: pre-install + helm.sh/hook-weight: "3" + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} +type: Opaque +data: + SPEEDSCALE_API_KEY: {{ .Values.apiKey | b64enc }} + SPEEDSCALE_APP_URL: {{ .Values.appUrl | b64enc }} +{{ end }} diff --git a/charts/speedscale/speedscale-operator/2.2.342/templates/services.yaml b/charts/speedscale/speedscale-operator/2.2.342/templates/services.yaml new file mode 100644 index 0000000000..f9da2c25c1 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.342/templates/services.yaml @@ -0,0 +1,22 @@ +--- +apiVersion: v1 +kind: Service +metadata: + creationTimestamp: null + labels: + app: speedscale-operator + controlplane.speedscale.com/component: operator + name: speedscale-operator + namespace: {{ .Release.Namespace }} + {{- if .Values.globalAnnotations }} + annotations: {{ toYaml .Values.globalAnnotations | nindent 4 }} + {{- end }} +spec: + ports: + - port: 443 + protocol: TCP + selector: + app: speedscale-operator + controlplane.speedscale.com/component: operator +status: + loadBalancer: {} diff --git a/charts/speedscale/speedscale-operator/2.2.342/templates/tls.yaml b/charts/speedscale/speedscale-operator/2.2.342/templates/tls.yaml new file mode 100644 index 0000000000..4a24562884 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.342/templates/tls.yaml @@ -0,0 +1,183 @@ +{{- $crt := "" -}} +{{- $key := "" -}} +{{- $s := (lookup "v1" "Secret" .Release.Namespace "speedscale-certs") -}} +{{- if $s -}} +{{- $crt = index $s.data "tls.crt" | b64dec -}} +{{- $key = index $s.data "tls.key" | b64dec -}} +{{ else }} +{{- $cert := genCA "Speedscale" 3650 -}} +{{- $crt = $cert.Cert -}} +{{- $key = $cert.Key -}} +{{- end -}} +--- +apiVersion: batch/v1 +kind: Job +metadata: + annotations: + helm.sh/hook: pre-install + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded + helm.sh/hook-weight: "5" + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} + creationTimestamp: null + name: speedscale-operator-create-jks + namespace: {{ .Release.Namespace }} + labels: + {{- if .Values.globalLabels }} +{{ toYaml .Values.globalLabels | indent 4}} + {{- end }} +spec: + backoffLimit: 0 + ttlSecondsAfterFinished: 30 + template: + metadata: + annotations: + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 8}} + {{- end }} + creationTimestamp: null + labels: + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 8}} + {{- end }} + spec: + containers: + - args: + - |- + keytool -keystore /usr/lib/jvm/jre/lib/security/cacerts -importcert -noprompt -trustcacerts -storepass changeit -alias speedscale -file /etc/ssl/speedscale/tls.crt + kubectl -n ${POD_NAMESPACE} delete secret speedscale-jks || true + kubectl -n ${POD_NAMESPACE} create secret generic speedscale-jks --from-file=cacerts.jks=/usr/lib/jvm/jre/lib/security/cacerts + + # in case we're in istio + curl -X POST http://127.0.0.1:15000/quitquitquit || true + command: + - sh + - -c + volumeMounts: + - mountPath: /etc/ssl/speedscale + name: speedscale-tls-out + readOnly: true + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + envFrom: + - secretRef: + name: '{{ ne .Values.apiKeySecret "" | ternary .Values.apiKeySecret "speedscale-apikey" }}' + optional: false + image: '{{ .Values.image.registry }}/amazoncorretto' + imagePullPolicy: {{ .Values.image.pullPolicy }} + name: create-jks + resources: {} + restartPolicy: Never + serviceAccountName: speedscale-operator-provisioning + volumes: + - name: speedscale-tls-out + secret: + secretName: speedscale-certs + {{- if .Values.affinity }} + affinity: {{ toYaml .Values.affinity | nindent 8 }} + {{- end }} + {{- if .Values.tolerations }} + tolerations: {{ toYaml .Values.tolerations | nindent 8 }} + {{- end }} + {{- if .Values.nodeSelector }} + nodeSelector: {{ toYaml .Values.nodeSelector | nindent 8 }} + {{- end }} +--- +apiVersion: v1 +automountServiceAccountToken: true +kind: ServiceAccount +metadata: + annotations: + helm.sh/hook: pre-install + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded + helm.sh/hook-weight: "1" + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} + creationTimestamp: null + labels: + app: speedscale-operator + controlplane.speedscale.com/component: operator + name: speedscale-operator-provisioning + namespace: {{ .Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + annotations: + helm.sh/hook: pre-install + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded + helm.sh/hook-weight: "2" + creationTimestamp: null + name: speedscale-operator-provisioning +rules: +- apiGroups: + - "" + resources: + - secrets + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + - validatingwebhookconfigurations + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + annotations: + helm.sh/hook: pre-install + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded + helm.sh/hook-weight: "3" + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} + creationTimestamp: null + name: speedscale-operator-provisioning +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: speedscale-operator-provisioning +subjects: +- kind: ServiceAccount + name: speedscale-operator-provisioning + namespace: {{ .Release.Namespace }} +--- +apiVersion: v1 +kind: Secret +metadata: + annotations: + helm.sh/hook: pre-install + helm.sh/hook-delete-policy: before-hook-creation + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} + creationTimestamp: null + name: speedscale-certs + namespace: {{ .Release.Namespace }} +type: kubernetes.io/tls +data: + tls.crt: {{ $crt | b64enc }} + tls.key: {{ $key | b64enc }} diff --git a/charts/speedscale/speedscale-operator/2.2.342/values.yaml b/charts/speedscale/speedscale-operator/2.2.342/values.yaml new file mode 100644 index 0000000000..19554b2d6c --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.342/values.yaml @@ -0,0 +1,133 @@ +# An API key is required to connect to the Speedscale cloud. +# If you need a key email support@speedscale.com. +apiKey: "" + +# A secret name can be referenced instead of the api key itself. +# The secret must be of the format: +# +# type: Opaque +# data: +# SPEEDSCALE_API_KEY: +# SPEEDSCALE_APP_URL: +apiKeySecret: "" + +# Speedscale domain to use. +appUrl: "app.speedscale.com" + +# The name of your cluster. +clusterName: "my-cluster" + +# Speedscale components image settings. +image: + registry: gcr.io/speedscale + tag: v2.2.342 + pullPolicy: Always + +# Log level for Speedscale components. +logLevel: "info" + +# Namespaces to be watched by Speedscale Operator as a list of names. +namespaceSelector: [] + +# Instructs operator to deploy resources necessary to interact with your cluster from the Speedscale dashboard. +dashboardAccess: true + +# Filter Rule to apply to the Speedscale Forwarder +filterRule: "standard" + +# Data Loss Prevention settings. +dlp: + # Instructs operator to enable data loss prevention features + enabled: false + + # Configuration for data loss prevention + config: "standard" + +# If the operator pod/webhooks need to be on the host network. +# This is only needed if the control plane cannot connect directly to a pod +# for eg. if Calico is used as EKS's default networking +# https://docs.tigera.io/calico/3.25/getting-started/kubernetes/managed-public-cloud/eks#install-eks-with-calico-networking +hostNetwork: false + +# A set of annotations to be applied to all Speedscale related deployments, +# services, jobs, pods, etc. +# +# Example: +# annotation.first: value +# annotation.second: value +globalAnnotations: {} + +# A set of labels to be applied to all Speedscale related deployments, +# services, jobs, pods, etc. +# +# Example: +# label1: value +# label2: value +globalLabels: {} + +# A full affinity object as detailed: https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes-using-node-affinity +affinity: {} + +# The list of tolerations as detailed: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ +tolerations: [] + +# A nodeselector object as detailed: https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes/ +nodeSelector: {} + +# Deploy a demo app at startup. Set this to an empty string to not deploy. +# Valid values: ["java", ""] +deployDemo: "java" + +# Proxy connection settings if required by your network. These translate to standard proxy environment +# variables HTTP_PROXY, HTTPS_PROXY, and NO_PROXY +http_proxy: "" +https_proxy: "" +no_proxy: "" + +# control if sidecar init containers should run with privileged set +privilegedSidecars: false + +# control if the sidecar should enable/disable use of the smart dns lookup feature (requires NET_ADMIN) +disableSidecarSmartReverseDNS: false + +# Operator settings. These limits are recommended unless you have a cluster +# with a very large number of workloads (for eg. 10k+ deployments, replicasets, etc.). +operator: + resources: + limits: + cpu: 500m + memory: 512Mi + requests: + cpu: 100m + memory: 128Mi + +# Default sidecar settings. Example: +# sidecar: +# resources: +# limits: +# cpu: 500m +# memory: 512Mi +# ephemeral-storage: 100Mi +# requests: +# cpu: 10m +# memory: 32Mi +# ephemeral-storage: 100Mi +# ignore_src_hosts: example.com, example.org +# ignore_src_ips: 8.8.8.8, 1.1.1.1 +# ignore_dst_hosts: example.com, example.org +# ignore_dst_ips: 8.8.8.8, 1.1.1.1 +# insert_init_first: false +# tls_out: false +# reinitialize_iptables: false +sidecar: {} + +# Forwarder settings +# forwarder: +# resources: +# limits: +# cpu: 500m +# memory: 500M +# requests: +# cpu: 300m +# memory: 250M +forwarder: {} diff --git a/charts/traefik/traefik/31.0.0/.helmignore b/charts/traefik/traefik/31.0.0/.helmignore new file mode 100644 index 0000000000..9c42ddd90e --- /dev/null +++ b/charts/traefik/traefik/31.0.0/.helmignore @@ -0,0 +1,2 @@ +tests/ +crds/kustomization.yaml diff --git a/charts/traefik/traefik/31.0.0/Changelog.md b/charts/traefik/traefik/31.0.0/Changelog.md new file mode 100644 index 0000000000..3c57ee7779 --- /dev/null +++ b/charts/traefik/traefik/31.0.0/Changelog.md @@ -0,0 +1,9257 @@ +# Change Log + +## 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.0.0/Chart.yaml b/charts/traefik/traefik/31.0.0/Chart.yaml new file mode 100644 index 0000000000..e7da63966d --- /dev/null +++ b/charts/traefik/traefik/31.0.0/Chart.yaml @@ -0,0 +1,33 @@ +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 + set allowEmptyServices to true by default\"\n- \"feat(Traefik Hub): update CRDs + to v1.7.0\"\n- \"chore(release): \U0001F680 publish v31.0.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.2 +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.0.0 diff --git a/charts/traefik/traefik/31.0.0/EXAMPLES.md b/charts/traefik/traefik/31.0.0/EXAMPLES.md new file mode 100644 index 0000000000..8cad755953 --- /dev/null +++ b/charts/traefik/traefik/31.0.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.0.0/Guidelines.md b/charts/traefik/traefik/31.0.0/Guidelines.md new file mode 100644 index 0000000000..19937d4da6 --- /dev/null +++ b/charts/traefik/traefik/31.0.0/Guidelines.md @@ -0,0 +1,92 @@ +# 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). + +Optionality +All non-critical features (Features not mandatory to starting Traefik) in the helm chart must be optional. All non-critical features should be disabled (commented out) in the values.yaml file. All optional non-critical features should be disabled (commented out) in the values.yaml file, and have a comment # (Optional) in the line above. This allows minimal configuration, and ease of extension. + +## Feature Example + +```yaml +image: + # -- Traefik image host registry + registry: docker.io +``` + +This feature is expected and therefore is defined clearly in the values.yaml file. + +## Optional Feature Example + +```yaml +# storage: +# controlNode: +# type: emptyDir +``` + +This feature is optional, non-critical, and therefore is commented out by default in the values.yaml file. + +To allow this, template blocks that use this need to recursively test for existence of values before using them: + +```yaml +{{- if .Values.storage}} + {{- if .Values.storage.controlNode }} + //code + {{ .Values.storage.controlNode.type }} + {{- end }} +{{- end }} +``` + +The non-critical feature defaults should be populated so that they can be enabled by simply uncommenting the section in the values.yaml file. + +## Optional Non-Critical Feature Example + +```yaml +# storage: +# controlNode: +# type: emptyDir +# # (Optional) +# # volume: 1Gi +``` + +The volume option is clearly optional, and non-critical. It is commented out (apart from the storage section comment block), and is also preceded by a comment of # (Optional) in the preceding line. This facilitates configuration, when the storage section is uncommented, the optional features are still disabled by default. + +Similar to non-critical features, these options need to be tested for existence before use in the template. + +Note +There can be optional values in critical features. These should just be added as an uncommented non-critical feature: + +```yaml +image: + name: traefik + tag: 2.0.0 + # (Optional) + # pullPolicy: IfNotPresent +``` + +Also, the first value under the primary value key does not require an optional comment: + +```yaml +# ports: +# http: 80 +# # (Optional) +# # https: 443 +``` + +This is because if the main subkey is not defined, the entirety of the feature is optional. + +## 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.0.0/LICENSE b/charts/traefik/traefik/31.0.0/LICENSE new file mode 100644 index 0000000000..907ff83212 --- /dev/null +++ b/charts/traefik/traefik/31.0.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.0.0/README.md b/charts/traefik/traefik/31.0.0/README.md new file mode 100644 index 0000000000..cd963c1994 --- /dev/null +++ b/charts/traefik/traefik/31.0.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.0.0/VALUES.md b/charts/traefik/traefik/31.0.0/VALUES.md new file mode 100644 index 0000000000..5b7bac1c02 --- /dev/null +++ b/charts/traefik/traefik/31.0.0/VALUES.md @@ -0,0 +1,278 @@ +# traefik + +![Version: 31.0.0](https://img.shields.io/badge/Version-31.0.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v3.1.2](https://img.shields.io/badge/AppVersion-v3.1.2-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 | `nil` | 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.enabled | bool | `true` | Enable deployment | +| 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.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.replicas | int | `1` | Number of pods of the deployment (only applies when kind == Deployment) | +| deployment.runtimeClassName | string | `nil` | 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 | string | `nil` | 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":null,"namespacePolicy":null,"port":8000,"protocol":"HTTP"}}` | Define listeners | +| gateway.listeners.web.hostname | string | `nil` | 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 | `nil` | Set a custom name to gateway | +| gateway.namespace | string | `nil` | 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 | string | `nil` | Additional gatewayClass labels (e.g. for filtering gateway objects by custom labels) | +| gatewayClass.name | string | `nil` | 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 | `nil` | WebHook admission server listen address. Default: "0.0.0.0:9943". | +| hub.apimanagement.admission.secretName | string | `nil` | Certificate of the WebHook admission server. Default: "hub-agent-cert". | +| hub.apimanagement.enabled | string | `nil` | 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 | `nil` | Endpoints of the Redis instances to connect to. Default: "". | +| hub.ratelimit.redis.password | string | `nil` | The password to use when connecting to Redis endpoints. Default: "". | +| hub.ratelimit.redis.sentinel.masterset | string | `nil` | Name of the set of main nodes to use for main selection. Required when using Sentinel. Default: "". | +| hub.ratelimit.redis.sentinel.password | string | `nil` | Password to use for sentinel authentication (can be different from endpoint password). Default: "". | +| hub.ratelimit.redis.sentinel.username | string | `nil` | Username to use for sentinel authentication (can be different from endpoint username). Default: "". | +| hub.ratelimit.redis.timeout | string | `nil` | Timeout applied on connection with redis. Default: "0s". | +| hub.ratelimit.redis.tls.ca | string | `nil` | Path to the certificate authority used for the secured connection. | +| hub.ratelimit.redis.tls.cert | string | `nil` | Path to the public certificate used for the secure connection. | +| hub.ratelimit.redis.tls.insecureSkipVerify | string | `nil` | When insecureSkipVerify is set to true, the TLS connection accepts any certificate presented by the server. Default: false. | +| hub.ratelimit.redis.tls.key | string | `nil` | Path to the private key used for the secure connection. | +| hub.ratelimit.redis.username | string | `nil` | The username to use when connecting to Redis endpoints. Default: "". | +| hub.sendlogs | string | `nil` | | +| hub.token | string | `nil` | 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}` | 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 | `nil` | | +| 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 | string | `nil` | 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"` | Available modes: keep, drop, redact. | +| 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"` | Available modes: keep, drop, redact. | +| 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.general.format | string | `nil` | Set [logs format](https://doc.traefik.io/traefik/observability/logs/#format) @default common | +| logs.general.level | string | `"INFO"` | Alternative logging levels are DEBUG, PANIC, FATAL, ERROR, WARN, and INFO. | +| metrics.addInternals | string | `nil` | | +| 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 | string | `nil` | 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 | `nil` | Format: ://:. Default: http://localhost:4318/v1/metrics | +| metrics.otlp.grpc.insecure | string | `nil` | Allows reporter to send metrics to the OpenTelemetry Collector without using a secured protocol. | +| metrics.otlp.grpc.tls.ca | string | `nil` | The path to the certificate authority, it defaults to the system bundle. | +| metrics.otlp.grpc.tls.cert | string | `nil` | The path to the public certificate. When using this option, setting the key option is required. | +| metrics.otlp.grpc.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.grpc.tls.key | string | `nil` | 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 | `nil` | Format: ://:. Default: http://localhost:4318/v1/metrics | +| metrics.otlp.http.headers | string | `nil` | Additional headers sent with metrics by the reporter to the OpenTelemetry Collector. | +| metrics.otlp.http.tls.ca | string | `nil` | The path to the certificate authority, it defaults to the system bundle. | +| metrics.otlp.http.tls.cert | string | `nil` | 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 | `nil` | The path to the private key. When using this option, setting the cert option is required. | +| metrics.otlp.pushInterval | string | `nil` | Interval at which metrics are sent to the OpenTelemetry Collector. Default: 10s | +| 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.prometheusRule.additionalLabels | string | `nil` | | +| metrics.prometheus.prometheusRule.enabled | bool | `false` | Enable optional CR for Prometheus Operator. See EXAMPLES.md for more details. | +| metrics.prometheus.prometheusRule.namespace | string | `nil` | | +| metrics.prometheus.service.annotations | string | `nil` | | +| metrics.prometheus.service.enabled | string | `nil` | Create a dedicated metrics service to use with ServiceMonitor | +| metrics.prometheus.service.labels | string | `nil` | | +| metrics.prometheus.serviceMonitor.additionalLabels | string | `nil` | | +| metrics.prometheus.serviceMonitor.enableHttp2 | string | `nil` | | +| metrics.prometheus.serviceMonitor.enabled | bool | `false` | Enable optional CR for Prometheus Operator. See EXAMPLES.md for more details. | +| metrics.prometheus.serviceMonitor.followRedirects | string | `nil` | | +| metrics.prometheus.serviceMonitor.honorLabels | string | `nil` | | +| metrics.prometheus.serviceMonitor.honorTimestamps | string | `nil` | | +| metrics.prometheus.serviceMonitor.interval | string | `nil` | | +| metrics.prometheus.serviceMonitor.jobLabel | string | `nil` | | +| metrics.prometheus.serviceMonitor.metricRelabelings | string | `nil` | | +| metrics.prometheus.serviceMonitor.namespace | string | `nil` | | +| metrics.prometheus.serviceMonitor.namespaceSelector | string | `nil` | | +| metrics.prometheus.serviceMonitor.relabelings | string | `nil` | | +| metrics.prometheus.serviceMonitor.scrapeTimeout | string | `nil` | | +| namespaceOverride | string | `nil` | 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.name | string | `"data"` | | +| persistence.path | string | `"/data"` | | +| persistence.size | string | `"128Mi"` | | +| podDisruptionBudget | object | `{"enabled":null,"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.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.port | int | `8000` | | +| ports.web.protocol | string | `"TCP"` | | +| 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.expose.default | bool | `true` | | +| ports.websecure.exposedPort | int | `443` | | +| 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 middlewares: - namespace-name1@kubernetescrd - namespace-name2@kubernetescrd | +| ports.websecure.port | int | `8443` | | +| ports.websecure.protocol | string | `"TCP"` | | +| ports.websecure.tls.certResolver | string | `""` | | +| ports.websecure.tls.domains | list | `[]` | | +| ports.websecure.tls.enabled | bool | `true` | | +| ports.websecure.tls.options | string | `""` | | +| ports.websecure.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 | +| priorityClassName | string | `""` | [Pod Priority and Preemption](https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/) | +| providers.file.content | string | `nil` | 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 | `nil` | 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 | string | `nil` | 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 | `nil` | 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 | string | `nil` | Defines whether to use Native Kubernetes load-balancing mode by default. | +| providers.kubernetesIngress.publishedService.enabled | bool | `false` | | +| rbac | object | `{"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 | string | `nil` | 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":null,"otlp":{"enabled":false,"grpc":{"enabled":false,"endpoint":null,"insecure":null,"tls":{"ca":null,"cert":null,"insecureSkipVerify":null,"key":null}},"http":{"enabled":false,"endpoint":null,"headers":null,"tls":{"ca":null,"cert":null,"insecureSkipVerify":null,"key":null}}}}` | https://doc.traefik.io/traefik/observability/tracing/overview/ | +| tracing.addInternals | string | `nil` | 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 | `nil` | Format: ://:. Default: http://localhost:4318/v1/metrics | +| tracing.otlp.grpc.insecure | string | `nil` | Allows reporter to send metrics to the OpenTelemetry Collector without using a secured protocol. | +| tracing.otlp.grpc.tls.ca | string | `nil` | The path to the certificate authority, it defaults to the system bundle. | +| tracing.otlp.grpc.tls.cert | string | `nil` | The path to the public certificate. When using this option, setting the key option is required. | +| tracing.otlp.grpc.tls.insecureSkipVerify | string | `nil` | 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 | `nil` | 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 | `nil` | Format: ://:. Default: http://localhost:4318/v1/metrics | +| tracing.otlp.http.headers | string | `nil` | Additional headers sent with metrics by the reporter to the OpenTelemetry Collector. | +| tracing.otlp.http.tls.ca | string | `nil` | The path to the certificate authority, it defaults to the system bundle. | +| tracing.otlp.http.tls.cert | string | `nil` | The path to the public certificate. When using this option, setting the key option is required. | +| tracing.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. | +| tracing.otlp.http.tls.key | string | `nil` | 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.0.0/app-readme.md b/charts/traefik/traefik/31.0.0/app-readme.md new file mode 100644 index 0000000000..7289af5d02 --- /dev/null +++ b/charts/traefik/traefik/31.0.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.0.0/crds/gateway-standard-install-v1.1.0.yaml b/charts/traefik/traefik/31.0.0/crds/gateway-standard-install-v1.1.0.yaml new file mode 100644 index 0000000000..fcb65e66f3 --- /dev/null +++ b/charts/traefik/traefik/31.0.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.0.0/crds/hub.traefik.io_accesscontrolpolicies.yaml b/charts/traefik/traefik/31.0.0/crds/hub.traefik.io_accesscontrolpolicies.yaml new file mode 100644 index 0000000000..821f969b67 --- /dev/null +++ b/charts/traefik/traefik/31.0.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.0.0/crds/hub.traefik.io_apiaccesses.yaml b/charts/traefik/traefik/31.0.0/crds/hub.traefik.io_apiaccesses.yaml new file mode 100644 index 0000000000..d1b9998c93 --- /dev/null +++ b/charts/traefik/traefik/31.0.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.0.0/crds/hub.traefik.io_apiportals.yaml b/charts/traefik/traefik/31.0.0/crds/hub.traefik.io_apiportals.yaml new file mode 100644 index 0000000000..bc04170169 --- /dev/null +++ b/charts/traefik/traefik/31.0.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.0.0/crds/hub.traefik.io_apiratelimits.yaml b/charts/traefik/traefik/31.0.0/crds/hub.traefik.io_apiratelimits.yaml new file mode 100644 index 0000000000..8e328d3c54 --- /dev/null +++ b/charts/traefik/traefik/31.0.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.0.0/crds/hub.traefik.io_apis.yaml b/charts/traefik/traefik/31.0.0/crds/hub.traefik.io_apis.yaml new file mode 100644 index 0000000000..a7b9e5e600 --- /dev/null +++ b/charts/traefik/traefik/31.0.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.0.0/crds/hub.traefik.io_apiversions.yaml b/charts/traefik/traefik/31.0.0/crds/hub.traefik.io_apiversions.yaml new file mode 100644 index 0000000000..97184effee --- /dev/null +++ b/charts/traefik/traefik/31.0.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.0.0/crds/traefik.io_ingressroutes.yaml b/charts/traefik/traefik/31.0.0/crds/traefik.io_ingressroutes.yaml new file mode 100644 index 0000000000..7b23dba43d --- /dev/null +++ b/charts/traefik/traefik/31.0.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.14.0 + 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.0/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.0/routing/routers/#rule + type: string + middlewares: + description: |- + Middlewares defines the list of references to Middleware resources. + More info: https://doc.traefik.io/traefik/v3.0/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.0/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.0/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.0/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.0/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.0/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.0/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.0/https/tls/#tls-options + properties: + name: + description: |- + Name defines the name of the referenced TLSOption. + More info: https://doc.traefik.io/traefik/v3.0/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.0/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.0/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.0/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.0.0/crds/traefik.io_ingressroutetcps.yaml b/charts/traefik/traefik/31.0.0/crds/traefik.io_ingressroutetcps.yaml new file mode 100644 index 0000000000..f3eea5e744 --- /dev/null +++ b/charts/traefik/traefik/31.0.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.14.0 + 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.0/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.0/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.0/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.0/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 is not supported APIVersion traefik.io/v1, 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.0/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.0/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.0/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.0/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.0/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.0.0/crds/traefik.io_ingressrouteudps.yaml b/charts/traefik/traefik/31.0.0/crds/traefik.io_ingressrouteudps.yaml new file mode 100644 index 0000000000..19bbfe62eb --- /dev/null +++ b/charts/traefik/traefik/31.0.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.14.0 + 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.0/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.0.0/crds/traefik.io_middlewares.yaml b/charts/traefik/traefik/31.0.0/crds/traefik.io_middlewares.yaml new file mode 100644 index 0000000000..0d005e64df --- /dev/null +++ b/charts/traefik/traefik/31.0.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.14.0 + 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.0/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.0/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.0/middlewares/http/basicauth/ + properties: + headerField: + description: |- + HeaderField defines a header field to store the authenticated user. + More info: https://doc.traefik.io/traefik/v3.0/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.0/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.0/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.0/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.0/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.0/middlewares/http/digestauth/ + properties: + headerField: + description: |- + HeaderField defines a header field to store the authenticated user. + More info: https://doc.traefik.io/traefik/v3.0/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.0/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.0/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.0/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.0/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.0/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.0/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.0/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.0/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.0/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.0/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.0/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.0/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.0/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.0/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.0/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.0/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.0/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.0/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.0/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.0/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.0/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.0/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.0.0/crds/traefik.io_middlewaretcps.yaml b/charts/traefik/traefik/31.0.0/crds/traefik.io_middlewaretcps.yaml new file mode 100644 index 0000000000..250ac1b127 --- /dev/null +++ b/charts/traefik/traefik/31.0.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.14.0 + 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.0/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.0/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.0/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.0.0/crds/traefik.io_serverstransports.yaml b/charts/traefik/traefik/31.0.0/crds/traefik.io_serverstransports.yaml new file mode 100644 index 0000000000..287943fbf8 --- /dev/null +++ b/charts/traefik/traefik/31.0.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.14.0 + 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.0/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.0.0/crds/traefik.io_serverstransporttcps.yaml b/charts/traefik/traefik/31.0.0/crds/traefik.io_serverstransporttcps.yaml new file mode 100644 index 0000000000..b255d3296c --- /dev/null +++ b/charts/traefik/traefik/31.0.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.14.0 + 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.0/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.0.0/crds/traefik.io_tlsoptions.yaml b/charts/traefik/traefik/31.0.0/crds/traefik.io_tlsoptions.yaml new file mode 100644 index 0000000000..2380e8ef66 --- /dev/null +++ b/charts/traefik/traefik/31.0.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.14.0 + 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.0/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.0/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.0/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.0/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.0.0/crds/traefik.io_tlsstores.yaml b/charts/traefik/traefik/31.0.0/crds/traefik.io_tlsstores.yaml new file mode 100644 index 0000000000..15c4951ea7 --- /dev/null +++ b/charts/traefik/traefik/31.0.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.14.0 + 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.0/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.0.0/crds/traefik.io_traefikservices.yaml b/charts/traefik/traefik/31.0.0/crds/traefik.io_traefikservices.yaml new file mode 100644 index 0000000000..7a0f7daf31 --- /dev/null +++ b/charts/traefik/traefik/31.0.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.14.0 + 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.0/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.0/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.0/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.0/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.0/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.0.0/templates/NOTES.txt b/charts/traefik/traefik/31.0.0/templates/NOTES.txt new file mode 100644 index 0000000000..a1a10bfb36 --- /dev/null +++ b/charts/traefik/traefik/31.0.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.0.0/templates/_helpers.tpl b/charts/traefik/traefik/31.0.0/templates/_helpers.tpl new file mode 100644 index 0000000000..2183f84ab1 --- /dev/null +++ b/charts/traefik/traefik/31.0.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 }} + {{- if eq (typeOf $config.redirectTo) "string" }} + {{- fail "ERROR: Syntax of `ports.web.redirectTo` has changed to `ports.web.redirectTo.port`. Details in PR #934." }} + {{- end }} + {{- $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.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 }} + {{- if .general.format }} + - "--log.format={{ .general.format }}" + {{- end }} + {{- if .general.filePath }} + - "--log.filePath={{ .general.filePath }}" + {{- end }} + {{- if and (or (eq .general.format "common") (not .general.format)) (eq .general.noColor true) }} + - "--log.noColor={{ .general.noColor }}" + {{- end }} + {{- if and .general.level (not (has (.general.level | upper) (list "DEBUG" "PANIC" "FATAL" "ERROR" "WARN" "INFO"))) }} + {{- fail "ERROR: .Values.logs.level must be DEBUG, PANIC, FATAL, ERROR, WARN, and INFO" }} + {{- end }} + {{- if .general.level }} + - "--log.level={{ .general.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.0.0/templates/_service-metrics.tpl b/charts/traefik/traefik/31.0.0/templates/_service-metrics.tpl new file mode 100644 index 0000000000..d16a3629d6 --- /dev/null +++ b/charts/traefik/traefik/31.0.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.0.0/templates/_service.tpl b/charts/traefik/traefik/31.0.0/templates/_service.tpl new file mode 100644 index 0000000000..27d5bc4777 --- /dev/null +++ b/charts/traefik/traefik/31.0.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.0.0/templates/daemonset.yaml b/charts/traefik/traefik/31.0.0/templates/daemonset.yaml new file mode 100644 index 0000000000..5be6a0a251 --- /dev/null +++ b/charts/traefik/traefik/31.0.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.0.0/templates/deployment.yaml b/charts/traefik/traefik/31.0.0/templates/deployment.yaml new file mode 100644 index 0000000000..3e9c8ad78f --- /dev/null +++ b/charts/traefik/traefik/31.0.0/templates/deployment.yaml @@ -0,0 +1,54 @@ +{{/* 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 }} + +{{- if ne (typeOf .Values.experimental.plugins) "map[string]interface {}" }} + {{- fail (printf "ERROR: .Values.experimental.plugins should be a map (%s provided) !" (typeOf .Values.experimental.plugins)) }} +{{- 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.0.0/templates/extra-objects.yaml b/charts/traefik/traefik/31.0.0/templates/extra-objects.yaml new file mode 100644 index 0000000000..fb38e97734 --- /dev/null +++ b/charts/traefik/traefik/31.0.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.0.0/templates/gateway.yaml b/charts/traefik/traefik/31.0.0/templates/gateway.yaml new file mode 100644 index 0000000000..13696dffce --- /dev/null +++ b/charts/traefik/traefik/31.0.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.0.0/templates/gatewayclass.yaml b/charts/traefik/traefik/31.0.0/templates/gatewayclass.yaml new file mode 100644 index 0000000000..7f98c1ed43 --- /dev/null +++ b/charts/traefik/traefik/31.0.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.0.0/templates/hpa.yaml b/charts/traefik/traefik/31.0.0/templates/hpa.yaml new file mode 100644 index 0000000000..cfa1e5a492 --- /dev/null +++ b/charts/traefik/traefik/31.0.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.0.0/templates/hub-admission-controller.yaml b/charts/traefik/traefik/31.0.0/templates/hub-admission-controller.yaml new file mode 100644 index 0000000000..6a64516000 --- /dev/null +++ b/charts/traefik/traefik/31.0.0/templates/hub-admission-controller.yaml @@ -0,0 +1,250 @@ +{{- 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-edge-ingress + labels: + {{- include "traefik.labels" . | nindent 4 }} +webhooks: + - name: admission.traefik.svc + clientConfig: + service: + name: admission + namespace: {{ template "traefik.namespace" . }} + path: /edge-ingress + caBundle: {{ $cert.Cert }} + sideEffects: None + admissionReviewVersions: + - v1 + rules: + - operations: + - CREATE + - UPDATE + - DELETE + apiGroups: + - hub.traefik.io + apiVersions: + - v1alpha1 + resources: + - edgeingresses + scope: Namespaced + +--- +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.gateway + clientConfig: + service: + name: admission + namespace: {{ template "traefik.namespace" . }} + path: /api-gateway + caBundle: {{ $cert.Cert }} + sideEffects: None + admissionReviewVersions: + - v1 + rules: + - operations: + - CREATE + - UPDATE + - DELETE + apiGroups: + - hub.traefik.io + apiVersions: + - v1alpha1 + resources: + - apigateways + - 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.collection + clientConfig: + service: + name: admission + namespace: {{ template "traefik.namespace" . }} + path: /api-collection + caBundle: {{ $cert.Cert }} + sideEffects: None + admissionReviewVersions: + - v1 + rules: + - operations: + - CREATE + - UPDATE + - DELETE + apiGroups: + - hub.traefik.io + apiVersions: + - v1alpha1 + resources: + - apicollections + - 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.rate-limit + clientConfig: + service: + name: admission + namespace: {{ template "traefik.namespace" . }} + path: /api-rate-limit + caBundle: {{ $cert.Cert }} + sideEffects: None + admissionReviewVersions: + - v1 + rules: + - operations: + - CREATE + - UPDATE + - DELETE + apiGroups: + - hub.traefik.io + apiVersions: + - v1alpha1 + resources: + - apiratelimits + - 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.0.0/templates/hub-apiportal.yaml b/charts/traefik/traefik/31.0.0/templates/hub-apiportal.yaml new file mode 100644 index 0000000000..246b127ed5 --- /dev/null +++ b/charts/traefik/traefik/31.0.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.0.0/templates/ingressclass.yaml b/charts/traefik/traefik/31.0.0/templates/ingressclass.yaml new file mode 100644 index 0000000000..6a8ff81994 --- /dev/null +++ b/charts/traefik/traefik/31.0.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.0.0/templates/ingressroute.yaml b/charts/traefik/traefik/31.0.0/templates/ingressroute.yaml new file mode 100644 index 0000000000..2f35abb2a4 --- /dev/null +++ b/charts/traefik/traefik/31.0.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.0.0/templates/poddisruptionbudget.yaml b/charts/traefik/traefik/31.0.0/templates/poddisruptionbudget.yaml new file mode 100644 index 0000000000..f1716397ce --- /dev/null +++ b/charts/traefik/traefik/31.0.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.0.0/templates/prometheusrules.yaml b/charts/traefik/traefik/31.0.0/templates/prometheusrules.yaml new file mode 100644 index 0000000000..3231aba6c2 --- /dev/null +++ b/charts/traefik/traefik/31.0.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.0.0/templates/provider-file-cm.yaml b/charts/traefik/traefik/31.0.0/templates/provider-file-cm.yaml new file mode 100644 index 0000000000..139a5a6ab9 --- /dev/null +++ b/charts/traefik/traefik/31.0.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.0.0/templates/pvc.yaml b/charts/traefik/traefik/31.0.0/templates/pvc.yaml new file mode 100644 index 0000000000..7ab96f960a --- /dev/null +++ b/charts/traefik/traefik/31.0.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.0.0/templates/rbac/clusterrole.yaml b/charts/traefik/traefik/31.0.0/templates/rbac/clusterrole.yaml new file mode 100644 index 0000000000..3fa943e543 --- /dev/null +++ b/charts/traefik/traefik/31.0.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.0.0/templates/rbac/role.yaml b/charts/traefik/traefik/31.0.0/templates/rbac/role.yaml new file mode 100644 index 0000000000..e81aaa8a65 --- /dev/null +++ b/charts/traefik/traefik/31.0.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: + 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: + +## Tracing +# -- https://doc.traefik.io/traefik/observability/tracing/overview/ +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: +- "--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: 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. + # + # -- 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: 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 + # 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 + # + # -- 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 + port: 8443 + # hostPort: 8443 + # containerPort: 8443 + 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 + ## -- 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 + http3: + 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 + transport: + respondingTimeouts: + readTimeout: + writeTimeout: + idleTimeout: + lifeCycle: + requestAcceptGraceTimeout: + graceTimeOut: + keepAliveMaxRequests: + keepAliveMaxTime: + # + ## Set TLS at the entrypoint + ## 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: [] + 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 + # 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 + 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: + 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: [ "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: + enabled: false + +# -- 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) +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: + 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: diff --git a/index.yaml b/index.yaml index 019cd5db6e..19ab7937dc 100644 --- a/index.yaml +++ b/index.yaml @@ -6169,6 +6169,47 @@ entries: - assets/confluent/confluent-for-kubernetes-0.771.29.tgz version: 0.771.29 consul: + - annotations: + artifacthub.io/images: | + - name: consul + image: hashicorp/consul:1.19.2 + - name: consul-k8s-control-plane + image: hashicorp/consul-k8s-control-plane:1.5.3 + - name: consul-dataplane + image: hashicorp/consul-dataplane:1.5.3 + - name: envoy + image: envoyproxy/envoy:v1.25.11 + artifacthub.io/license: MPL-2.0 + artifacthub.io/links: | + - name: Documentation + url: https://www.consul.io/docs/k8s + - name: hashicorp/consul + url: https://github.com/hashicorp/consul + - name: hashicorp/consul-k8s + url: https://github.com/hashicorp/consul-k8s + artifacthub.io/prerelease: "false" + artifacthub.io/signKey: | + fingerprint: C874011F0AB405110D02105534365D9472D7468F + url: https://keybase.io/hashicorp/pgp_keys.asc + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Hashicorp Consul + catalog.cattle.io/kube-version: '>=1.22.0-0' + catalog.cattle.io/release-name: consul + apiVersion: v2 + appVersion: 1.19.2 + created: "2024-09-04T00:51:36.312573797Z" + description: Official HashiCorp Consul Chart + digest: 3bb9923e798b77a58e075d5077bc9a9db8455078b12f8dcb116ab28140ab5063 + home: https://www.consul.io + icon: file://assets/icons/consul.png + kubeVersion: '>=1.22.0-0' + name: consul + sources: + - https://github.com/hashicorp/consul + - https://github.com/hashicorp/consul-k8s + urls: + - assets/hashicorp/consul-1.5.3.tgz + version: 1.5.3 - annotations: artifacthub.io/images: | - name: consul @@ -21743,6 +21784,36 @@ entries: - assets/avesha/kubeslice-worker-1.1.1.tgz version: 1.1.1 kuma: + - annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Kuma + catalog.cattle.io/namespace: kuma-system + catalog.cattle.io/release-name: kuma + apiVersion: v2 + appVersion: 2.8.3 + created: "2024-09-04T00:51:38.16886733Z" + description: A Helm chart for the Kuma Control Plane + digest: e0bb31a66ccf2322f46f5bba962013ef413303342c44a655a93b15a1d2be6c52 + home: https://github.com/kumahq/kuma + icon: file://assets/icons/kuma.svg + keywords: + - service mesh + - control plane + maintainers: + - email: jakub.dyszkiewicz@konghq.com + name: Jakub Dyszkiewicz + url: https://github.com/jakubdyszkiewicz + - email: charly.molter@konghq.com + name: Charly Molter + url: https://github.com/lahabana + - email: michael.beaumont@konghq.com + name: Mike Beaumont + url: https://github.com/michaelbeaumont + name: kuma + type: application + urls: + - assets/kuma/kuma-2.8.3.tgz + version: 2.8.3 - annotations: catalog.cattle.io/certified: partner catalog.cattle.io/display-name: Kuma @@ -26527,6 +26598,95 @@ entries: - assets/f5/nginx-ingress-1.0.2.tgz version: 1.0.2 nri-bundle: + - annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: New Relic + catalog.cattle.io/release-name: nri-bundle + apiVersion: v2 + created: "2024-09-04T00:51:39.072792051Z" + dependencies: + - condition: infrastructure.enabled,newrelic-infrastructure.enabled + name: newrelic-infrastructure + repository: file://./charts/newrelic-infrastructure + version: 3.34.4 + - condition: prometheus.enabled,nri-prometheus.enabled + name: nri-prometheus + repository: file://./charts/nri-prometheus + version: 2.1.18 + - condition: newrelic-prometheus-agent.enabled + name: newrelic-prometheus-agent + repository: file://./charts/newrelic-prometheus-agent + version: 1.14.3 + - condition: webhook.enabled,nri-metadata-injection.enabled + name: nri-metadata-injection + repository: file://./charts/nri-metadata-injection + version: 4.21.0 + - condition: metrics-adapter.enabled,newrelic-k8s-metrics-adapter.enabled + name: newrelic-k8s-metrics-adapter + repository: file://./charts/newrelic-k8s-metrics-adapter + version: 1.11.2 + - condition: ksm.enabled,kube-state-metrics.enabled + name: kube-state-metrics + repository: file://./charts/kube-state-metrics + version: 5.12.1 + - condition: kubeEvents.enabled,nri-kube-events.enabled + name: nri-kube-events + repository: file://./charts/nri-kube-events + version: 3.10.5 + - condition: logging.enabled,newrelic-logging.enabled + name: newrelic-logging + repository: file://./charts/newrelic-logging + version: 1.22.4 + - condition: newrelic-pixie.enabled + name: newrelic-pixie + repository: file://./charts/newrelic-pixie + version: 2.1.4 + - condition: k8s-agents-operator.enabled + name: k8s-agents-operator + repository: file://./charts/k8s-agents-operator + version: 0.11.0 + - alias: pixie-chart + condition: pixie-chart.enabled + name: pixie-operator-chart + repository: file://./charts/pixie-operator-chart + version: 0.1.6 + - condition: newrelic-infra-operator.enabled + name: newrelic-infra-operator + repository: file://./charts/newrelic-infra-operator + version: 2.11.2 + description: Groups together the individual charts for the New Relic Kubernetes + solution for a more comfortable deployment. + digest: 64849875c56d1f43fd864fe5d72d430f772f7cacf481a667b4b6066ed3d62c0e + home: https://github.com/newrelic/helm-charts + icon: file://assets/icons/nri-bundle.svg + keywords: + - infrastructure + - newrelic + - monitoring + maintainers: + - name: juanjjaramillo + url: https://github.com/juanjjaramillo + - name: csongnr + url: https://github.com/csongnr + - name: dbudziwojskiNR + url: https://github.com/dbudziwojskiNR + name: nri-bundle + sources: + - https://github.com/newrelic/nri-bundle/ + - https://github.com/newrelic/nri-bundle/tree/master/charts/nri-bundle + - https://github.com/newrelic/nri-kubernetes/tree/master/charts/newrelic-infrastructure + - https://github.com/newrelic/nri-prometheus/tree/master/charts/nri-prometheus + - https://github.com/newrelic/newrelic-prometheus-configurator/tree/master/charts/newrelic-prometheus-agent + - https://github.com/newrelic/k8s-metadata-injection/tree/master/charts/nri-metadata-injection + - https://github.com/newrelic/newrelic-k8s-metrics-adapter/tree/master/charts/newrelic-k8s-metrics-adapter + - https://github.com/newrelic/nri-kube-events/tree/master/charts/nri-kube-events + - https://github.com/newrelic/helm-charts/tree/master/charts/newrelic-logging + - https://github.com/newrelic/helm-charts/tree/master/charts/newrelic-pixie + - https://github.com/newrelic/newrelic-infra-operator/tree/master/charts/newrelic-infra-operator + - https://github.com/newrelic/k8s-agents-operator/tree/master/charts/k8s-agents-operator + urls: + - assets/new-relic/nri-bundle-5.0.91.tgz + version: 5.0.91 - annotations: catalog.cattle.io/certified: partner catalog.cattle.io/display-name: New Relic @@ -35005,6 +35165,37 @@ entries: - assets/btp/sextant-2.2.21.tgz version: 2.2.21 speedscale-operator: + - annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Speedscale Operator + catalog.cattle.io/kube-version: '>= 1.17.0-0' + catalog.cattle.io/release-name: speedscale-operator + apiVersion: v1 + appVersion: 2.2.342 + created: "2024-09-04T00:51:39.676988906Z" + description: Stress test your APIs with real world scenarios. Collect and replay + traffic without scripting. + digest: b4a1eb84645c15861f63555d19254e9c51c28d5bfeed0598cdabc01b8644bfce + home: https://speedscale.com + icon: file://assets/icons/speedscale-operator.png + keywords: + - speedscale + - test + - testing + - regression + - reliability + - load + - replay + - network + - traffic + kubeVersion: '>= 1.17.0-0' + maintainers: + - email: support@speedscale.com + name: Speedscale Support + name: speedscale-operator + urls: + - assets/speedscale/speedscale-operator-2.2.342.tgz + version: 2.2.342 - annotations: catalog.cattle.io/certified: partner catalog.cattle.io/display-name: Speedscale Operator @@ -39154,6 +39345,43 @@ entries: - assets/intel/tcs-issuer-0.1.0.tgz version: 0.1.0 traefik: + - 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 + set allowEmptyServices to true by default\"\n- \"feat(Traefik Hub): update + CRDs to v1.7.0\"\n- \"chore(release): \U0001F680 publish v31.0.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.2 + created: "2024-09-04T00:51:40.008607546Z" + description: A Traefik based Kubernetes ingress controller + digest: 3dad98c6e2982c67ae4c2a5d53d851c4feee3f612550b975384f0939e6eaee16 + 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.0.0.tgz + version: 31.0.0 - annotations: artifacthub.io/changes: "- \"fix: disable default HTTPS listener for gateway\"\n- \"fix(Gateway API): wildcard support in hostname\"\n- \"fix(Gateway API): @@ -41342,4 +41570,4 @@ entries: urls: - assets/netfoundry/ziti-host-1.5.1.tgz version: 1.5.1 -generated: "2024-09-03T00:50:14.380046656Z" +generated: "2024-09-04T00:51:35.207093482Z"

    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�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 literal 0 HcmV?d00001 diff --git a/assets/traefik/traefik-31.0.0.tgz b/assets/traefik/traefik-31.0.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..e33367f4257f3467922fe75c9e5a1fcb1d8d922f GIT binary patch literal 230201 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%0AeG%5_Ap7L>ZCq!oxX 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%0Aqu-zl`ay%mnOd|z*wCk+VP)otO4oaxn|z!y2w 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

    V2l0RV`(+xa?W;%ny`T1O`5E_3YCG~IBOgd&}= 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