From b51aa4ed4e119c598aa7c6613811c7ea649829df Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 25 Oct 2024 00:41:45 +0000 Subject: [PATCH] Added chart versions: airlock/microgateway: - 4.4.0 airlock/microgateway-cni: - 4.4.0 buoyant/linkerd-control-plane: - 2024.10.4 buoyant/linkerd-crds: - 2024.10.4 dynatrace/dynatrace-operator: - 1.3.2 f5/f5-bigip-ctlr: - 0.0.33 percona/pxc-db: - 1.15.1 percona/pxc-operator: - 1.15.1 --- assets/airlock/microgateway-4.4.0.tgz | Bin 0 -> 82252 bytes assets/airlock/microgateway-cni-4.4.0.tgz | Bin 0 -> 10943 bytes .../linkerd-control-plane-2024.10.3.tgz | Bin 31590 -> 31579 bytes .../linkerd-control-plane-2024.10.4.tgz | Bin 0 -> 31837 bytes assets/buoyant/linkerd-crds-2024.10.4.tgz | Bin 0 -> 125877 bytes assets/dynatrace/dynatrace-operator-1.3.2.tgz | Bin 0 -> 56455 bytes assets/f5/f5-bigip-ctlr-0.0.3301.tgz | Bin 0 -> 13027 bytes assets/percona/pxc-db-1.15.1.tgz | Bin 0 -> 18563 bytes assets/percona/pxc-operator-1.15.1.tgz | Bin 0 -> 30973 bytes .../microgateway-cni/4.4.0/.helmignore | 27 + .../airlock/microgateway-cni/4.4.0/Chart.yaml | 43 + .../airlock/microgateway-cni/4.4.0/README.md | 137 + .../microgateway-cni/4.4.0/gke-values.yaml | 4 + .../4.4.0/openshift-values.yaml | 15 + .../microgateway-cni/4.4.0/questions.yml | 18 + .../4.4.0/templates/NOTES.txt | 15 + .../4.4.0/templates/_helpers.tpl | 101 + .../4.4.0/templates/clusterrole.yaml | 22 + .../4.4.0/templates/clusterrolebinding.yaml | 20 + .../4.4.0/templates/configmap.yaml | 22 + .../4.4.0/templates/daemonset.yaml | 136 + .../network-attachment-definition.yaml | 13 + .../4.4.0/templates/scc-role.yaml | 22 + .../4.4.0/templates/scc-rolebinding.yaml | 20 + .../4.4.0/templates/serviceaccount.yaml | 13 + .../4.4.0/templates/tests/rbac.yaml | 64 + .../4.4.0/templates/tests/test-install.yaml | 103 + .../microgateway-cni/4.4.0/values.schema.json | 225 + .../microgateway-cni/4.4.0/values.yaml | 85 + charts/airlock/microgateway/4.4.0/.helmignore | 28 + charts/airlock/microgateway/4.4.0/Chart.yaml | 44 + charts/airlock/microgateway/4.4.0/README.md | 186 + .../airlock/microgateway/4.4.0/app-readme.md | 28 + ...cesscontrols.microgateway.airlock.com.yaml | 501 + ...ntsecurities.microgateway.airlock.com.yaml | 139 + ...ritypolicies.microgateway.airlock.com.yaml | 476 + .../denyrules.microgateway.airlock.com.yaml | 1812 +++ ...nvoyclusters.microgateway.airlock.com.yaml | 58 + ...nfigurations.microgateway.airlock.com.yaml | 185 + ...yhttpfilters.microgateway.airlock.com.yaml | 58 + .../graphqls.microgateway.airlock.com.yaml | 88 + ...aderrewrites.microgateway.airlock.com.yaml | 2083 +++ ...propagations.microgateway.airlock.com.yaml | 151 + .../crds/jwks.microgateway.airlock.com.yaml | 294 + .../crds/limits.microgateway.airlock.com.yaml | 651 + ...idcproviders.microgateway.airlock.com.yaml | 342 + ...lyingparties.microgateway.airlock.com.yaml | 235 + .../openapis.microgateway.airlock.com.yaml | 167 + .../parsers.microgateway.airlock.com.yaml | 358 + ...disproviders.microgateway.airlock.com.yaml | 232 + ...ionhandlings.microgateway.airlock.com.yaml | 89 + ...ecargateways.microgateway.airlock.com.yaml | 758 ++ .../telemetries.microgateway.airlock.com.yaml | 96 + .../4.4.0/dashboards/blockLogs.json | 441 + .../4.4.0/dashboards/blockMetrics.json | 751 ++ .../4.4.0/dashboards/headerLogs.json | 378 + .../4.4.0/dashboards/license.json | 1063 ++ .../4.4.0/dashboards/logOnlyLogs.json | 382 + .../4.4.0/dashboards/logOnlyMetrics.json | 621 + .../4.4.0/dashboards/overview.json | 1134 ++ .../microgateway/4.4.0/templates/NOTES.txt | 61 + .../microgateway/4.4.0/templates/_helpers.tpl | 153 + .../templates/operator/_operator_helpers.tpl | 42 + .../4.4.0/templates/operator/_rbac.gen.tpl | 206 + .../templates/operator/_webhooks.gen.tpl | 399 + .../4.4.0/templates/operator/configmap.yaml | 405 + .../operator/dashboard-configmap.yaml | 28 + .../4.4.0/templates/operator/deployment.yaml | 143 + .../templates/operator/manager-role.yaml | 33 + .../operator/manager-rolebinding.yaml | 45 + .../templates/operator/metrics-service.yaml | 47 + .../templates/operator/mutating-webhook.yaml | 28 + .../4.4.0/templates/operator/podmonitor.yaml | 27 + .../4.4.0/templates/operator/role.yaml | 45 + .../4.4.0/templates/operator/rolebinding.yaml | 20 + .../templates/operator/selfsigned-issuer.yaml | 13 + .../templates/operator/serviceaccount.yaml | 13 + .../templates/operator/servicemonitor.yaml | 60 + .../operator/serving-certificate.yaml | 19 + .../operator/validating-webhook.yaml | 28 + .../templates/operator/webhook-service.yaml | 23 + .../4.4.0/templates/operator/xds-service.yaml | 24 + .../4.4.0/templates/tests/rbac.yaml | 143 + .../4.4.0/templates/tests/service.yaml | 23 + .../4.4.0/templates/tests/statefulset.yaml | 56 + .../4.4.0/templates/tests/test-install.yaml | 227 + .../microgateway/4.4.0/values.schema.json | 572 + charts/airlock/microgateway/4.4.0/values.yaml | 237 + .../2024.10.3/Chart.yaml | 1 - .../2024.10.4/.helmignore | 22 + .../2024.10.4/Chart.lock | 6 + .../2024.10.4/Chart.yaml | 29 + .../linkerd-control-plane/2024.10.4/README.md | 321 + .../2024.10.4/README.md.gotmpl | 133 + .../2024.10.4/app-readme.md | 14 + .../2024.10.4/charts/partials/.helmignore | 21 + .../2024.10.4/charts/partials/Chart.yaml | 5 + .../2024.10.4/charts/partials/README.md | 9 + .../charts/partials/README.md.gotmpl | 14 + .../charts/partials/templates/NOTES.txt | 0 .../charts/partials/templates/_affinity.tpl | 38 + .../partials/templates/_capabilities.tpl | 16 + .../charts/partials/templates/_debug.tpl | 15 + .../charts/partials/templates/_helpers.tpl | 14 + .../charts/partials/templates/_metadata.tpl | 17 + .../partials/templates/_network-validator.tpl | 45 + .../partials/templates/_nodeselector.tpl | 4 + .../partials/templates/_proxy-config-ann.tpl | 18 + .../charts/partials/templates/_proxy-init.tpl | 98 + .../charts/partials/templates/_proxy.tpl | 271 + .../partials/templates/_pull-secrets.tpl | 6 + .../charts/partials/templates/_resources.tpl | 28 + .../partials/templates/_tolerations.tpl | 4 + .../charts/partials/templates/_trace.tpl | 5 + .../charts/partials/templates/_validate.tpl | 19 + .../charts/partials/templates/_volumes.tpl | 41 + .../2024.10.4/charts/partials/values.yaml | 0 .../2024.10.4/questions.yaml | 19 + .../2024.10.4/templates/NOTES.txt | 19 + .../2024.10.4/templates/config-rbac.yaml | 16 + .../2024.10.4/templates/config.yaml | 39 + .../2024.10.4/templates/destination-rbac.yaml | 336 + .../2024.10.4/templates/destination.yaml | 447 + .../2024.10.4/templates/heartbeat-rbac.yaml | 78 + .../2024.10.4/templates/heartbeat.yaml | 101 + .../2024.10.4/templates/identity-rbac.yaml | 49 + .../2024.10.4/templates/identity.yaml | 277 + .../2024.10.4/templates/namespace.yaml | 18 + .../2024.10.4/templates/podmonitor.yaml | 128 + .../templates/proxy-injector-rbac.yaml | 120 + .../2024.10.4/templates/proxy-injector.yaml | 227 + .../2024.10.4/templates/psp.yaml | 119 + .../2024.10.4/values-ha.yaml | 63 + .../2024.10.4/values.yaml | 664 + .../linkerd-crds/2024.10.4/.helmignore | 22 + .../buoyant/linkerd-crds/2024.10.4/Chart.lock | 6 + .../buoyant/linkerd-crds/2024.10.4/Chart.yaml | 26 + .../buoyant/linkerd-crds/2024.10.4/README.md | 73 + .../linkerd-crds/2024.10.4/README.md.gotmpl | 59 + .../linkerd-crds/2024.10.4/app-readme.md | 9 + .../2024.10.4/charts/partials/.helmignore | 21 + .../2024.10.4/charts/partials/Chart.yaml | 5 + .../2024.10.4/charts/partials/README.md | 9 + .../charts/partials/README.md.gotmpl | 14 + .../charts/partials/templates/NOTES.txt | 0 .../charts/partials/templates/_affinity.tpl | 38 + .../partials/templates/_capabilities.tpl | 16 + .../charts/partials/templates/_debug.tpl | 15 + .../charts/partials/templates/_helpers.tpl | 14 + .../charts/partials/templates/_metadata.tpl | 17 + .../partials/templates/_network-validator.tpl | 45 + .../partials/templates/_nodeselector.tpl | 4 + .../partials/templates/_proxy-config-ann.tpl | 18 + .../charts/partials/templates/_proxy-init.tpl | 98 + .../charts/partials/templates/_proxy.tpl | 271 + .../partials/templates/_pull-secrets.tpl | 6 + .../charts/partials/templates/_resources.tpl | 28 + .../partials/templates/_tolerations.tpl | 4 + .../charts/partials/templates/_trace.tpl | 5 + .../charts/partials/templates/_validate.tpl | 19 + .../charts/partials/templates/_volumes.tpl | 41 + .../2024.10.4/charts/partials/values.yaml | 0 .../2024.10.4/templates/NOTES.txt | 6 + .../gateway.networking.k8s.io_grpcroutes.yaml | 1507 +++ .../gateway.networking.k8s.io_httproutes.yaml | 3881 ++++++ .../gateway.networking.k8s.io_tcproutes.yaml | 533 + .../gateway.networking.k8s.io_tlsroutes.yaml | 582 + .../policy/authorization-policy.yaml | 99 + .../templates/policy/egress-network.yaml | 123 + .../2024.10.4/templates/policy/httproute.yaml | 5328 ++++++++ .../policy/meshtls-authentication.yaml | 87 + .../policy/network-authentication.yaml | 53 + .../policy/server-authorization.yaml | 266 + .../2024.10.4/templates/policy/server.yaml | 319 + .../2024.10.4/templates/serviceprofile.yaml | 274 + .../templates/workload/external-workload.yaml | 303 + .../linkerd-crds/2024.10.4/values.yaml | 3 + .../dynatrace-operator/1.3.2/.helmignore | 25 + .../dynatrace-operator/1.3.2/Chart.yaml | 23 + .../dynatrace-operator/1.3.2/README.md | 48 + .../dynatrace-operator/1.3.2/app-readme.md | 5 + .../dynatrace-operator/1.3.2/logo.png | Bin 0 -> 9908 bytes .../dynatrace-operator/1.3.2/questions.yml | 236 + .../activegate/clusterrole-activegate.yaml | 47 + .../activegate/serviceaccount-activegate.yaml | 20 + .../Common/crd/dynatrace-operator-crd.yaml | 5783 ++++++++ .../templates/Common/csi/clusterrole-csi.yaml | 47 + .../1.3.2/templates/Common/csi/csidriver.yaml | 29 + .../1.3.2/templates/Common/csi/daemonset.yaml | 280 + .../templates/Common/csi/priority-class.yaml | 23 + .../1.3.2/templates/Common/csi/role-csi.yaml | 70 + .../Common/csi/serviceaccount-csi.yaml | 22 + .../edge-connect/serviceaccount-operator.yaml | 20 + .../clusterrole-kubernetes-monitoring.yaml | 114 + .../serviceaccount-kubernetes-monitoring.yaml | 20 + .../Common/oneagent/clusterrole-oneagent.yaml | 45 + .../oneagent/serviceaccount-oneagent.yaml | 21 + .../Common/operator/clusterrole-operator.yaml | 109 + .../Common/operator/deployment-operator.yaml | 111 + .../Common/operator/role-operator.yaml | 170 + .../operator/serviceaccount-operator.yaml | 20 + .../Common/webhook/clusterrole-webhook.yaml | 102 + .../Common/webhook/deployment-webhook.yaml | 138 + .../webhook/mutatingwebhookconfiguration.yaml | 58 + .../webhook/poddisruptionbudget-webhook.yaml | 11 + .../Common/webhook/role-webhook.yaml | 70 + .../templates/Common/webhook/service.yaml | 27 + .../webhook/serviceaccount-webhook.yaml | 21 + .../validatingwebhookconfiguration.yaml | 104 + .../1.3.2/templates/NOTES.txt | 10 + .../1.3.2/templates/_csidriver.tpl | 74 + .../1.3.2/templates/_helpers.tpl | 53 + .../1.3.2/templates/_labels.tpl | 102 + .../1.3.2/templates/_platform.tpl | 84 + .../1.3.2/templates/application.yaml | 98 + .../dynatrace-operator/1.3.2/values.yaml | 196 + charts/f5/f5-bigip-ctlr/0.0.3301/.helmignore | 21 + charts/f5/f5-bigip-ctlr/0.0.3301/Chart.yaml | 26 + charts/f5/f5-bigip-ctlr/0.0.3301/README.md | 93 + .../f5/f5-bigip-ctlr/0.0.3301/app-readme.md | 87 + ...5-bigip-ctlr-customresourcedefinitions.yml | 1342 ++ .../f5/f5-bigip-ctlr/0.0.3301/questions.yaml | 75 + .../0.0.3301/templates/NOTES.txt | 6 + .../0.0.3301/templates/_helpers.tpl | 64 + .../templates/f5-bigip-ctlr-clusterrole.yaml | 111 + .../f5-bigip-ctlr-clusterrolebinding.yaml | 23 + .../templates/f5-bigip-ctlr-deploy.yaml | 139 + .../f5-bigip-ctlr-ingress-class.yaml | 12 + .../templates/f5-bigip-ctlr-secrets.yaml | 19 + .../f5-bigip-ctlr-serviceaccount.yaml | 17 + charts/f5/f5-bigip-ctlr/0.0.3301/values.yaml | 90 + charts/percona/pxc-db/1.15.1/.helmignore | 22 + charts/percona/pxc-db/1.15.1/Chart.yaml | 21 + charts/percona/pxc-db/1.15.1/README.md | 331 + .../percona/pxc-db/1.15.1/templates/NOTES.txt | 56 + .../pxc-db/1.15.1/templates/_helpers.tpl | 77 + .../1.15.1/templates/cluster-secret.yaml | 27 + .../1.15.1/templates/cluster-ssl-secret.yaml | 42 + .../pxc-db/1.15.1/templates/cluster.yaml | 544 + .../pxc-db/1.15.1/templates/s3-secret.yaml | 16 + charts/percona/pxc-db/1.15.1/values.yaml | 713 + .../percona/pxc-operator/1.15.1/.helmignore | 22 + charts/percona/pxc-operator/1.15.1/Chart.yaml | 22 + .../percona/pxc-operator/1.15.1/LICENSE.txt | 13 + charts/percona/pxc-operator/1.15.1/README.md | 64 + .../percona/pxc-operator/1.15.1/crds/crd.yaml | 11129 ++++++++++++++++ .../pxc-operator/1.15.1/templates/NOTES.txt | 16 + .../1.15.1/templates/_helpers.tpl | 56 + .../1.15.1/templates/deployment.yaml | 119 + .../1.15.1/templates/namespace.yaml | 11 + .../1.15.1/templates/role-binding.yaml | 37 + .../pxc-operator/1.15.1/templates/role.yaml | 142 + .../percona/pxc-operator/1.15.1/values.yaml | 69 + index.yaml | 269 +- 254 files changed, 60611 insertions(+), 3 deletions(-) create mode 100644 assets/airlock/microgateway-4.4.0.tgz create mode 100644 assets/airlock/microgateway-cni-4.4.0.tgz create mode 100644 assets/buoyant/linkerd-control-plane-2024.10.4.tgz create mode 100644 assets/buoyant/linkerd-crds-2024.10.4.tgz create mode 100644 assets/dynatrace/dynatrace-operator-1.3.2.tgz create mode 100644 assets/f5/f5-bigip-ctlr-0.0.3301.tgz create mode 100644 assets/percona/pxc-db-1.15.1.tgz create mode 100644 assets/percona/pxc-operator-1.15.1.tgz create mode 100644 charts/airlock/microgateway-cni/4.4.0/.helmignore create mode 100644 charts/airlock/microgateway-cni/4.4.0/Chart.yaml create mode 100644 charts/airlock/microgateway-cni/4.4.0/README.md create mode 100644 charts/airlock/microgateway-cni/4.4.0/gke-values.yaml create mode 100644 charts/airlock/microgateway-cni/4.4.0/openshift-values.yaml create mode 100644 charts/airlock/microgateway-cni/4.4.0/questions.yml create mode 100644 charts/airlock/microgateway-cni/4.4.0/templates/NOTES.txt create mode 100644 charts/airlock/microgateway-cni/4.4.0/templates/_helpers.tpl create mode 100644 charts/airlock/microgateway-cni/4.4.0/templates/clusterrole.yaml create mode 100644 charts/airlock/microgateway-cni/4.4.0/templates/clusterrolebinding.yaml create mode 100644 charts/airlock/microgateway-cni/4.4.0/templates/configmap.yaml create mode 100644 charts/airlock/microgateway-cni/4.4.0/templates/daemonset.yaml create mode 100644 charts/airlock/microgateway-cni/4.4.0/templates/network-attachment-definition.yaml create mode 100644 charts/airlock/microgateway-cni/4.4.0/templates/scc-role.yaml create mode 100644 charts/airlock/microgateway-cni/4.4.0/templates/scc-rolebinding.yaml create mode 100644 charts/airlock/microgateway-cni/4.4.0/templates/serviceaccount.yaml create mode 100644 charts/airlock/microgateway-cni/4.4.0/templates/tests/rbac.yaml create mode 100644 charts/airlock/microgateway-cni/4.4.0/templates/tests/test-install.yaml create mode 100644 charts/airlock/microgateway-cni/4.4.0/values.schema.json create mode 100644 charts/airlock/microgateway-cni/4.4.0/values.yaml create mode 100644 charts/airlock/microgateway/4.4.0/.helmignore create mode 100644 charts/airlock/microgateway/4.4.0/Chart.yaml create mode 100644 charts/airlock/microgateway/4.4.0/README.md create mode 100644 charts/airlock/microgateway/4.4.0/app-readme.md create mode 100644 charts/airlock/microgateway/4.4.0/crds/accesscontrols.microgateway.airlock.com.yaml create mode 100644 charts/airlock/microgateway/4.4.0/crds/contentsecurities.microgateway.airlock.com.yaml create mode 100644 charts/airlock/microgateway/4.4.0/crds/contentsecuritypolicies.microgateway.airlock.com.yaml create mode 100644 charts/airlock/microgateway/4.4.0/crds/denyrules.microgateway.airlock.com.yaml create mode 100644 charts/airlock/microgateway/4.4.0/crds/envoyclusters.microgateway.airlock.com.yaml create mode 100644 charts/airlock/microgateway/4.4.0/crds/envoyconfigurations.microgateway.airlock.com.yaml create mode 100644 charts/airlock/microgateway/4.4.0/crds/envoyhttpfilters.microgateway.airlock.com.yaml create mode 100644 charts/airlock/microgateway/4.4.0/crds/graphqls.microgateway.airlock.com.yaml create mode 100644 charts/airlock/microgateway/4.4.0/crds/headerrewrites.microgateway.airlock.com.yaml create mode 100644 charts/airlock/microgateway/4.4.0/crds/identitypropagations.microgateway.airlock.com.yaml create mode 100644 charts/airlock/microgateway/4.4.0/crds/jwks.microgateway.airlock.com.yaml create mode 100644 charts/airlock/microgateway/4.4.0/crds/limits.microgateway.airlock.com.yaml create mode 100644 charts/airlock/microgateway/4.4.0/crds/oidcproviders.microgateway.airlock.com.yaml create mode 100644 charts/airlock/microgateway/4.4.0/crds/oidcrelyingparties.microgateway.airlock.com.yaml create mode 100644 charts/airlock/microgateway/4.4.0/crds/openapis.microgateway.airlock.com.yaml create mode 100644 charts/airlock/microgateway/4.4.0/crds/parsers.microgateway.airlock.com.yaml create mode 100644 charts/airlock/microgateway/4.4.0/crds/redisproviders.microgateway.airlock.com.yaml create mode 100644 charts/airlock/microgateway/4.4.0/crds/sessionhandlings.microgateway.airlock.com.yaml create mode 100644 charts/airlock/microgateway/4.4.0/crds/sidecargateways.microgateway.airlock.com.yaml create mode 100644 charts/airlock/microgateway/4.4.0/crds/telemetries.microgateway.airlock.com.yaml create mode 100644 charts/airlock/microgateway/4.4.0/dashboards/blockLogs.json create mode 100644 charts/airlock/microgateway/4.4.0/dashboards/blockMetrics.json create mode 100644 charts/airlock/microgateway/4.4.0/dashboards/headerLogs.json create mode 100644 charts/airlock/microgateway/4.4.0/dashboards/license.json create mode 100644 charts/airlock/microgateway/4.4.0/dashboards/logOnlyLogs.json create mode 100644 charts/airlock/microgateway/4.4.0/dashboards/logOnlyMetrics.json create mode 100644 charts/airlock/microgateway/4.4.0/dashboards/overview.json create mode 100644 charts/airlock/microgateway/4.4.0/templates/NOTES.txt create mode 100644 charts/airlock/microgateway/4.4.0/templates/_helpers.tpl create mode 100644 charts/airlock/microgateway/4.4.0/templates/operator/_operator_helpers.tpl create mode 100644 charts/airlock/microgateway/4.4.0/templates/operator/_rbac.gen.tpl create mode 100644 charts/airlock/microgateway/4.4.0/templates/operator/_webhooks.gen.tpl create mode 100644 charts/airlock/microgateway/4.4.0/templates/operator/configmap.yaml create mode 100644 charts/airlock/microgateway/4.4.0/templates/operator/dashboard-configmap.yaml create mode 100644 charts/airlock/microgateway/4.4.0/templates/operator/deployment.yaml create mode 100644 charts/airlock/microgateway/4.4.0/templates/operator/manager-role.yaml create mode 100644 charts/airlock/microgateway/4.4.0/templates/operator/manager-rolebinding.yaml create mode 100644 charts/airlock/microgateway/4.4.0/templates/operator/metrics-service.yaml create mode 100644 charts/airlock/microgateway/4.4.0/templates/operator/mutating-webhook.yaml create mode 100644 charts/airlock/microgateway/4.4.0/templates/operator/podmonitor.yaml create mode 100644 charts/airlock/microgateway/4.4.0/templates/operator/role.yaml create mode 100644 charts/airlock/microgateway/4.4.0/templates/operator/rolebinding.yaml create mode 100644 charts/airlock/microgateway/4.4.0/templates/operator/selfsigned-issuer.yaml create mode 100644 charts/airlock/microgateway/4.4.0/templates/operator/serviceaccount.yaml create mode 100644 charts/airlock/microgateway/4.4.0/templates/operator/servicemonitor.yaml create mode 100644 charts/airlock/microgateway/4.4.0/templates/operator/serving-certificate.yaml create mode 100644 charts/airlock/microgateway/4.4.0/templates/operator/validating-webhook.yaml create mode 100644 charts/airlock/microgateway/4.4.0/templates/operator/webhook-service.yaml create mode 100644 charts/airlock/microgateway/4.4.0/templates/operator/xds-service.yaml create mode 100644 charts/airlock/microgateway/4.4.0/templates/tests/rbac.yaml create mode 100644 charts/airlock/microgateway/4.4.0/templates/tests/service.yaml create mode 100644 charts/airlock/microgateway/4.4.0/templates/tests/statefulset.yaml create mode 100644 charts/airlock/microgateway/4.4.0/templates/tests/test-install.yaml create mode 100644 charts/airlock/microgateway/4.4.0/values.schema.json create mode 100644 charts/airlock/microgateway/4.4.0/values.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/.helmignore create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/Chart.lock create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/Chart.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/README.md create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/README.md.gotmpl create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/app-readme.md create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/.helmignore create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/Chart.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/README.md create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/README.md.gotmpl create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/NOTES.txt create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_affinity.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_capabilities.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_debug.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_helpers.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_metadata.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_network-validator.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_nodeselector.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_proxy-config-ann.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_proxy-init.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_proxy.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_pull-secrets.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_resources.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_tolerations.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_trace.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_validate.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_volumes.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/values.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/questions.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/templates/NOTES.txt create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/templates/config-rbac.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/templates/config.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/templates/destination-rbac.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/templates/destination.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/templates/heartbeat-rbac.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/templates/heartbeat.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/templates/identity-rbac.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/templates/identity.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/templates/namespace.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/templates/podmonitor.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/templates/proxy-injector-rbac.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/templates/proxy-injector.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/templates/psp.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/values-ha.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2024.10.4/values.yaml create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/.helmignore create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/Chart.lock create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/Chart.yaml create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/README.md create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/README.md.gotmpl create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/app-readme.md create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/charts/partials/.helmignore create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/charts/partials/Chart.yaml create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/charts/partials/README.md create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/charts/partials/README.md.gotmpl create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/NOTES.txt create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_affinity.tpl create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_capabilities.tpl create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_debug.tpl create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_helpers.tpl create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_metadata.tpl create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_network-validator.tpl create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_nodeselector.tpl create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_proxy-config-ann.tpl create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_proxy-init.tpl create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_proxy.tpl create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_pull-secrets.tpl create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_resources.tpl create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_tolerations.tpl create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_trace.tpl create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_validate.tpl create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_volumes.tpl create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/charts/partials/values.yaml create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/templates/NOTES.txt create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/templates/gateway.networking.k8s.io_grpcroutes.yaml create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/templates/gateway.networking.k8s.io_httproutes.yaml create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/templates/gateway.networking.k8s.io_tcproutes.yaml create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/templates/gateway.networking.k8s.io_tlsroutes.yaml create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/templates/policy/authorization-policy.yaml create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/templates/policy/egress-network.yaml create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/templates/policy/httproute.yaml create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/templates/policy/meshtls-authentication.yaml create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/templates/policy/network-authentication.yaml create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/templates/policy/server-authorization.yaml create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/templates/policy/server.yaml create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/templates/serviceprofile.yaml create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/templates/workload/external-workload.yaml create mode 100644 charts/buoyant/linkerd-crds/2024.10.4/values.yaml create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/.helmignore create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/Chart.yaml create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/README.md create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/app-readme.md create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/logo.png create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/questions.yml create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/activegate/clusterrole-activegate.yaml create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/activegate/serviceaccount-activegate.yaml create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/crd/dynatrace-operator-crd.yaml create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/csi/clusterrole-csi.yaml create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/csi/csidriver.yaml create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/csi/daemonset.yaml create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/csi/priority-class.yaml create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/csi/role-csi.yaml create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/csi/serviceaccount-csi.yaml create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/edge-connect/serviceaccount-operator.yaml create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/kubernetes-monitoring/clusterrole-kubernetes-monitoring.yaml create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/kubernetes-monitoring/serviceaccount-kubernetes-monitoring.yaml create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/oneagent/clusterrole-oneagent.yaml create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/oneagent/serviceaccount-oneagent.yaml create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/operator/clusterrole-operator.yaml create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/operator/deployment-operator.yaml create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/operator/role-operator.yaml create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/operator/serviceaccount-operator.yaml create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/clusterrole-webhook.yaml create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/deployment-webhook.yaml create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/mutatingwebhookconfiguration.yaml create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/poddisruptionbudget-webhook.yaml create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/role-webhook.yaml create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/service.yaml create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/serviceaccount-webhook.yaml create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/validatingwebhookconfiguration.yaml create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/NOTES.txt create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/_csidriver.tpl create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/_helpers.tpl create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/_labels.tpl create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/_platform.tpl create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/templates/application.yaml create mode 100644 charts/dynatrace/dynatrace-operator/1.3.2/values.yaml create mode 100644 charts/f5/f5-bigip-ctlr/0.0.3301/.helmignore create mode 100644 charts/f5/f5-bigip-ctlr/0.0.3301/Chart.yaml create mode 100644 charts/f5/f5-bigip-ctlr/0.0.3301/README.md create mode 100644 charts/f5/f5-bigip-ctlr/0.0.3301/app-readme.md create mode 100644 charts/f5/f5-bigip-ctlr/0.0.3301/crds/f5-bigip-ctlr-customresourcedefinitions.yml create mode 100644 charts/f5/f5-bigip-ctlr/0.0.3301/questions.yaml create mode 100644 charts/f5/f5-bigip-ctlr/0.0.3301/templates/NOTES.txt create mode 100644 charts/f5/f5-bigip-ctlr/0.0.3301/templates/_helpers.tpl create mode 100644 charts/f5/f5-bigip-ctlr/0.0.3301/templates/f5-bigip-ctlr-clusterrole.yaml create mode 100644 charts/f5/f5-bigip-ctlr/0.0.3301/templates/f5-bigip-ctlr-clusterrolebinding.yaml create mode 100644 charts/f5/f5-bigip-ctlr/0.0.3301/templates/f5-bigip-ctlr-deploy.yaml create mode 100644 charts/f5/f5-bigip-ctlr/0.0.3301/templates/f5-bigip-ctlr-ingress-class.yaml create mode 100644 charts/f5/f5-bigip-ctlr/0.0.3301/templates/f5-bigip-ctlr-secrets.yaml create mode 100644 charts/f5/f5-bigip-ctlr/0.0.3301/templates/f5-bigip-ctlr-serviceaccount.yaml create mode 100644 charts/f5/f5-bigip-ctlr/0.0.3301/values.yaml create mode 100644 charts/percona/pxc-db/1.15.1/.helmignore create mode 100644 charts/percona/pxc-db/1.15.1/Chart.yaml create mode 100644 charts/percona/pxc-db/1.15.1/README.md create mode 100644 charts/percona/pxc-db/1.15.1/templates/NOTES.txt create mode 100644 charts/percona/pxc-db/1.15.1/templates/_helpers.tpl create mode 100644 charts/percona/pxc-db/1.15.1/templates/cluster-secret.yaml create mode 100644 charts/percona/pxc-db/1.15.1/templates/cluster-ssl-secret.yaml create mode 100644 charts/percona/pxc-db/1.15.1/templates/cluster.yaml create mode 100644 charts/percona/pxc-db/1.15.1/templates/s3-secret.yaml create mode 100644 charts/percona/pxc-db/1.15.1/values.yaml create mode 100644 charts/percona/pxc-operator/1.15.1/.helmignore create mode 100644 charts/percona/pxc-operator/1.15.1/Chart.yaml create mode 100644 charts/percona/pxc-operator/1.15.1/LICENSE.txt create mode 100644 charts/percona/pxc-operator/1.15.1/README.md create mode 100644 charts/percona/pxc-operator/1.15.1/crds/crd.yaml create mode 100644 charts/percona/pxc-operator/1.15.1/templates/NOTES.txt create mode 100644 charts/percona/pxc-operator/1.15.1/templates/_helpers.tpl create mode 100644 charts/percona/pxc-operator/1.15.1/templates/deployment.yaml create mode 100644 charts/percona/pxc-operator/1.15.1/templates/namespace.yaml create mode 100644 charts/percona/pxc-operator/1.15.1/templates/role-binding.yaml create mode 100644 charts/percona/pxc-operator/1.15.1/templates/role.yaml create mode 100644 charts/percona/pxc-operator/1.15.1/values.yaml diff --git a/assets/airlock/microgateway-4.4.0.tgz b/assets/airlock/microgateway-4.4.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..0a7579ed95294e4923d70d8ec3ff6c67ce4cc353 GIT binary patch literal 82252 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 literal 0 HcmV?d00001 diff --git a/assets/buoyant/linkerd-control-plane-2024.10.3.tgz b/assets/buoyant/linkerd-control-plane-2024.10.3.tgz index a9b64c6c0ae3d2baf17ab62da71c445b507db465..58bb889eea3c2cb9f166ff48c55aaf03cc8a79d9 100644 GIT binary patch delta 29812 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 diff --git a/assets/buoyant/linkerd-control-plane-2024.10.4.tgz b/assets/buoyant/linkerd-control-plane-2024.10.4.tgz new file mode 100644 index 0000000000000000000000000000000000000000..fdcd25d0d7bf6c7e69e24349cc0221d844e8abae GIT binary patch literal 31837 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

9t$-axooxcL19EwA#su@qNUpN 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 literal 0 HcmV?d00001 diff --git a/assets/buoyant/linkerd-crds-2024.10.4.tgz b/assets/buoyant/linkerd-crds-2024.10.4.tgz new file mode 100644 index 0000000000000000000000000000000000000000..3ad4f86a54bbf837dc704f1b06f8ba1cf694e5d6 GIT binary patch literal 125877 zcmV)xK$E{8iwG0|00000|0w_~VMtOiV@ORlOnEsqVl!4SWK%V1T2nbTPgYhoO;>Dc zVQyr3R8em|NM&qo0POwSk{ijDCXDKuPvHY~50g{^c_53W%9$SR6sxL>)>SMTNtJ9y zP%9k~4#1m%@KAUllTdYy?c05`ulD_ZxBm(LNBGUxa`*5E#9=v@$!^)e&17OBBUbov z`8s|pllccxrqiTM_1VicFRR&(Z?a$AXFESXKY#K3IsE_m`FZ>QFP?qz;#U{XzdV2O z#fxWOzIgGg^NVLM&Y%5?o!@IOdfHZ3y!_SqCx5FwxWALe^ITQDk}B6%j~GjM#WS^< zCA_LKF_Y>n5oINpQlwX`;ANGIvh%T2>LTO2Y0fv|ihYl}ioJaMweEcU!*(I2x1!Wi z8vJ~lzJ$|%SW*F&*xhUZ+vEeeiVo9DXr;dCoZvS7I)Rmr@pG}t- zYiR4B;wq=5A=-yb#YQ0^|618zJyZ^sDdsOWTam9F1WHN#M z{Kzll<@x3F*~R(n`J?~n)ApaQb@2a8B_Hnf8zcPx{MqGYoBx0D;^N5vKS#TNekoT% zS6580`Q;Zcu6`??UoO8&zDl3-%QX4zZ=WqMzFdCwJbiKg{CV=#mtS1`HWBH?S3Euc z>hf~={L95}zkG2no_~4q?2BhFzFP1{E0K$mSLXhiT%KQEOwWHiz4-F|#rf6q%d0Og zXD=?!pFjWV@{2E~=U-f%pHGfwJgPO$|1Hn9Lf^XpIClOo&Y!i;|MT<97svDeIhx4% zA`{a$ zo(tO(nFIZwe`d3{s0Gem^Nr92PXznr7Z6zBKW2{}eXrC9nXf?A+Ne_ej{PLHth!W= z%d?fJDw(gQX8kLXp8cQ6$9m5HidW)}?`FBE?o??eb@t)6dM4FhR%MZt!2Zc{!r=LT zcK+>zkK%P7svDeS=!G(Pnle@*-t3?wZ#GU%P)_n(`m!X!V4)rRw6h5rR}yj zyS;eyLFVZdd%4w>+PoF0zke;3GMAu^Z$!mYUh%6(jJ2#z%+Q?jqA1m^NM|3m3sL66 zY%!^>y43&riK&(=zg?%ikY~j<%g!#ox_kzNdpGjgVwoc!JidZY`Hm;tRywJomtXVOx&gHL!RR;rZlzo#r%QTPQn z*FV2~^Aa|u9j31SrTP2!QdjV|BHNZc3p))zYniXM883rBJYq~Ist{MqPe)p-wa}p{ z<7o=Jm}hTFnOCBGsj}@R_uc(Bt@1a#T3@l5nSt3_=?caNe|IzZUF%o0S?Q{j`D&k# zJ211og=rAIUWo>|l-udSKi^*PtXT6)9DcGE8-8VY1N#xfNXpE%-nU&8GShe;?0F^uPd&mn^C)*>Q}iZ?ev<)m+W^O-9;UVed(MY;>JCr>%kYLDnwa%sl}d}T2u~xZC$F5&An^IxYWqVz@&wf zI?Uw&ce0ruWR{{Rg?2O0Ak54|JZEb0Zz8E??42kL8K&22n;DHfzZGT0N|C5lF8}6f zYsb?;<*QmWLCP`LBp8L!m$~6PRtobKw#@^2)^5yxK;?hg6xzM)tTvl%E~_01rE;;Y zRH@HWaVxSjEmu=sCTm%Vq}rC^%xDDA^&G|LP5SH7Di!*1qpy2X6gH3;2UfsHj!YU^ z24BTd)DvxfGPCpcn|JS-`!p;@d-h@8>lbys1ZKiyz7!=cBAj+ZQ{-u(WR3?b$)w2J z)33LSjjVJnXjE#(UV=UWVoj04<;Py->?Plb?4^DN5J0)vj&y!Q)-$Q(%XM%OTb=k_Z{HC7S*z*QH!&D z#bv&Co|qNUOfNwZuo5c>ry?^uhzFu$rJQ`~g(0DW|@p7c=(ijb^+Qa9MC8qnXWjIb+u~RkPU_MoAD$ z`4M!ecC)PiPG)Js%M|rrc5?G{cJsuj4Wj)g6uyc=@RBWd9Ta7yMYd!dN4}J$uJBcq z9vY*TPMVh@HLAu!HKd(zk-m4AmEn4RQQ%@M1@>wo7_XSoqbsIzf&3C~-^LMW?=^oh zHkKNQRXkavvJ!S-#=ccXXW-@v+~24m^gdA?0cJ)-1?B;3Xr>=e9G3ckH5eMVz zTz>k9-GCdz>!4W=*!KlK6P{W>6!%DD;ZHlLcCB%q?#;p-NC9TEKajz7US`dQBSc?yo7 z*_*+e%~?-Xy-q#LzqbWFr<1E(K_ptZC7Cqr)xYGs)U{QgcFbUt3)UmrpYaZg-5c_> zYm>qtUjGOKNmQ<7D$34YnjOO3Hli=j;K&=xV~$F0`B1H?CZ()IDLLZ{bt|m))tH

)yvGKwy)&j#oAK*TIK`SN-ni9y2N9r>pf;Ky@F@Ysu8%h8hAHodpFKUm#DH7 zx-ido-hnWdH@p;i^$uv%mo(1i$@LRZcy@&V1r2{y<={*h5k9y(d2^{??|a>oXQklj zj;#&f$bsGU^^=|a9y_xZmM=oL{HNRwuJ3&mv$~H~aAhpnHN5^$b!RQJ`jn-j66Hqb_4e?##15)%FP@q;Rb?v5D--4%DvlwRn9!!eOns}_$mr?;HU+GP38=2)C*C`glG6&)7$yvXYbzqg>5}OV+ zq)!nsTL+dBeDi^JiZG=QIDlxO{fXIC~1zpL$n=mYs?-n7GWIDOtkJX*TR4-)=-H6NnSx2{?UJ z$sSK1TVs_W3BeuaKRbzvJoR?MZjv+hT3PmAZ1berBC9hKYxi*1CfM+g--~=zt*_YS z7tgvLkhy!{qT|m6uPRaISL|O;=1=FdC%^j>pZ@K9`qiIL{xr4!dg^|9^1GAyZ1C?- zp8i9Zp3`lz@dJ1T9En}L*!!c6v@c8E*2oLsgKx!h)S~jnKgmk56s5=$;Vd22$j45$ zddm%evraMnq)%OQ^1~g^!BW=Jr)R--#822Srnbjjl8`g_N*!jbao>-wfdjcQfj|n>ubRWTJ~$#te)=7#2;jiqDdVVj*3#U;U|1}8$t#iV2K$S) z*j{UhUEf4y880*nl6%hHmTsukrY*^K(Kn=%wNe_oyAkCI4-v0m2<}+aQ&FjvFznd6 zO*_pkRyZ;@#i?N2EQex(z-r} zU4{X*%~M`Vp`B&X2uRMs&&q8k^o+eUbf%t8z%RRcS=xh+`&7c&*ul?%^W5HDW*yBs zRS?q9dur=Xds@&3kl+n>Z7jT7vb7bbuJK#F_O_)IS&BFITQ{CN<@95Lv9HUG6X0PMoCJNzxq3wA>Ub2*?r#Uc0>k)DnSf}ja5AzedT~NBL4HS$! z=1}_p?VU727`C~UhKIO*H;?Zm8o~GXxqNpw37o&?hTa>!Q6xNw-D|eSk*1t z>PnRV4X@l%Z420U;0ZJ@bP`>dO|vo#s1$8VRuYhb6k{a;_-?0g*zb?RhLLlfe=<99 z$+uZG99Y-=Vk4vr#%}o}e9&K`IF4$@{n=94lGv{kw*}BTH=n%2kg)j#x3%l=lT%#LUXpG{1bh&>qS$;gpRf}!!&rg% z$(R<8ty`D3pBvKwSVPeNhkyUq2N3evn4r%(9p!cN`^}zy((ZDeb;ov^`znm7csaBO zPCSDQUulMdO#N+n@*`+zF7Q}CAuP;`zI7SV&sbyW%pzP*|Ce2t>*@dUOJo229Zi@y(xBnCrjHyi_h9(=Lnl+~9el{_0{0KC zIS5-rQ0x=)+7cw#Z+c0#M9V;s91<6M+-cYj78-jgHdkU?bR6R`tXrfc36Q-cM+8fP z7&)}=4L#(%Q@O;G zob74{1Xxseg{URSUXo6-1ZMb0=WuC@#b7qIo6#}?+uvToJj=Y7N!x#!J#M>KJ2&k_ z@Cp^mAqkneKxwxLVd_p0R0INs>Ir>fX7Vw{a+V@dn+=HM{d7?dyD@8Z_ca&g8O}s5|<>nT1h* zYYnEwwlr&RK8XIcQ!u@ID`ziZsYA`U&P+A?DO35%Id9rW8NHmu9r7&7W( zZ5UE143A9udy1YHdp9M1bXJDgnCtVcH&ve2H)OwhrNWq?S4(zbrth+&f7T^Tm`)#v z>h3Y`$~>(_8Xx`9IrBZ%2h*H=4SJg^N>5#n_JV8cM(JKEtYyy+aKK`Utt-Ua{P^Fk zPJ0z*`u&f-OMI!y4X>`u#(#F%f6>d$c5}sEeDTGzFZyqKd}vsDHV16g!;5sy{b(0< zz%JVw1b5T|!*xHSf=e>EWM8$DmjcU9^P~L-v>EKxAZJ4p>=Iwqfggrw|BAORjFWJI zpS16f);%#Q?T+6c?R%n^WaRxZW^iNvWctGnnhjnA;bnq`^@bUJXxzL0yP~zqjeB9} zM7C{DE$fUG${_bHFuVn=!8L0Lf6;9F z5o0&L`N%u^-?*gTf|bZzM%=qIA(aF_Ug}e}w3*N52IA;|-rrs{AD54}G@or`-Dt7l zc_kCHSowVDZVOA87ajx*y5Kv_PVCe@ISsS1WyMbHV4ifdA2Y2K8KYG; zy>JW6<$f!cx-(#D%pnuJPm`y9U%$|pJ4?tYvCVYyf!n}lf7x8Vg;@Y=%l($iY+DMi zrZXUkcz*rdO671bZ+O0AVLuyU4$QBAj6+TtI<$t^)vG0Y%CqdLxok@*%x%B-2}{QZ zfDc$CVXtRe8BXoCc4&wQlgQ1PJ+)u2O1@mmWaJ{OBOVPAv&%79R3t*)LWs!CA6{So zbp7i4>)(I>&5acfhS{|cK5V-lcNj+L;)c-V%NJ@}vAgaV25cMt0h7%zCa*==EsB?z zchL-&XtYXT_>uzj=(z-eJs&wJ4b)IECg1YlC7BhnWlR7Z6;jPs#|JU)v z?X-*PPW8-0e{im8;;&ntu{$AG>&j*%K<-+w9lkhcg(wpjcYuefF9_X^edN&WDJ^x> zPrt1nV(oYp_DAXB$Ccp~qHA#3ACwf?Z>%EY% z?On@l<9-RhT{Qsp)*3$1r*v)3>Pk&vUFRv z1v$8GbfNMAF5y}xmm=9EnV7YEitKSCcn-b?o->bKHcHi|=qn~Z7NyXQ(T0JMDdzu< z`Y&B;g+Ew$ zhRtu2w_-W|bep|>6TA)n#GI)D!`)6>>rdqzt!}xXg!u%=KA(6^V8+H$hxc23g-td) z5N14CncGmT`6P(%3Q{cRlb%G7c5BQ^n864rUFOCF_)xRITu>p2u4SGi{yfYXvBm5Y zHWsqiZcV;A9*9RI+gx+jAClLZKiA#%%&_JQfg$!3*NbxWU0b;U`*>3j|d)IE! z>oydd*KUn<8x7Ta$RlyUbVtt2kR`X>rpORTPnvB`dayT{wAluY$L(f?vF`x0=Le9o zL>Vs^yzL?rq6z^r7wyu6cxb%(tT>AFJZO|f7=n9|M3Uri2wUt$$wh>U9JO7vbi3p#1FE+izb+Q2g@p**RU9i#@B@VlFvsq_FfTiip2Bq;xm$spT4q78vEElDA^UPTmROA@nMxGCh-`S_0hWy4qxbw|V(2#M`_i zDnWHapNThZJGY6095&bJEzgGh<=rjd7?@dTF%8TIb}r#Pl4ji*)<6adoZQO@ffJht z+j+NLXv2r|%Fn_lcQcrUJshJ*s$Xw~yDrRDnbuoHRy#legl|Pswd(_b4)Y1{g!$xK z{C7D5RW#Ju(KtazF2X@YQ&!5?dh3Ur+xRs$2i%*Z=U3|Ml_H-^{18fBnn+ zbF1*4J?0?j3Y*T9}+e+smli}~a<)OMcDCx51D%%=4!H|+sDkhXFF)BOzWN{fLs z(`IW&qgMdQe^<&%G|-QSV;lB5+~OV9OyJc2ni0^Ie9JIMd!4?1EhXGc+&wDc51@cQ z+*Up+-o70b@1x?)j*9nD@g8~gsCXY0@1x>#fpe^ zO3Gn6RuDuS;=>H#fp#~W#{w&6YXUYF$|1bRNS-bXf+fD((Nb-5hsi@oZ)mpw2-G73 zF9b_VRTNQ4dF}QdaLk~>YUVO~MXtBj!Ze?}6%6u26n+{QxM8VQI=F1150 zyTM=R4BP_tV<`eG?kHE-U8#N0^GKd|yFppYYzL`ZElg0q`sHAjcf0X^z#W@n@zlB4 zpVF;%m=ET1n*$(n05ol(2hrmRst4Kx^S*8H$?JbwM;DmhEnlnZjVL#=!n%=InvcF% zl+6@u68f?2*i5-A%Ol{*@ffdUqCW_&`O3#!-GqyC6BN7X1%mBvL8(~_JB0wksi?Tj zY_6A0J6dF76J=T52dHSKy10~8}dx6A0 zKsbP3joX>?oPCW$)(E*;!?@m=P1~rl7ERjl#WLp^*d%SuaEFTl)i>!*C(~ZOzW(7G z_Vw#`fTdQZx=#1W~3&z~F zqTB$YSCC>8H=pkTsxi|F25cp8&6gm0*)+P?83jM&%b&iPUY=iGOfSFq^7%Oe2)Kf! zwNKW`^u+xw&LhP@vPGyuL| z&v5NOlov-s5qGtN_)6@=HdqQ^1G+=tD~>y}Z=9>mkjl541@6kN7Gd28i%1tm(c!H6 zBX#GnjQydCu(mOfOZLqP7Qj*J1I#S#aZ3juh1eUxpXi1*(~LL@$>E4KY3QJa(tMDS-q!pELqn5$qiT6bhbv;47koQ_NN~$Rz3jx1CRB>^jTZ zMGt7|oG)V>R5yX!-9`5T{;LD@E=?coxBpoR1gq_3r^r6kS zB2Sg+p(``Ht+pj`$NGX|Yb;EgsQgxxD@ZVud0kVDLmncDq?hblI~~iE9wt0PDG+U| zhx(;)brSYj4DATudekxE&8@psC8j0VIihtmtj)I7u9i!q-~+}yu^Yq4`>S4s%?a{0 z+i~6~t1+Ko#fYu0)Fzq-gh!_1IN%Po#A!3oSar)#pmvN|HJc=g#S-WyrJhP5L8TAW zDtR5VO1=b}Gly4copD{-v`Bi?pQRI)_0S(=Q9!-bTvvjpr{e|W8og{4lYG7$gb*40vt>18=+Z;UffqIA2oTeBO#B#2|LsE`cv@6Savf(?_QGbXlZtY z)VT-e3_l%jrZYVA_1V4sw?@3v5cW4J6PnGKt9<%5Q7W4abZSA^Y{_CM zl`je{Gs7&$gB#6w#Ydi0;oOC_u~{k#fgTBqRaTTDk%T(SJa601tlX++%&wk8Yjc(0 z`D*M0SxY{%v+8CvRRT%n0@o1>;$NJf6YlI+ORO-ia#4eug?3N#QCYDyH~b1eIYM$( z3SK$+`TW#$SA;_M}u%?JjwF;khf!uR#E$(o&8$lBWLGTwowh!;gBhxfh59JR)SDp^|zWSgNm z_q}0W%cV#v(B@TX?$-e!N(EtB&eCo}qPL~FhO=F`C@G)*VZ|>SI&QE zx!z&4AJpRyC$!#hmD~socSx|s`nPJ?>mzCytw{RMNZf4GZ0%OeI?!E8xpmK&0qRN= zdJNmfVeuM)DHCQN1&6?eUG>Mw_A-nxu_p=4g6qpm2$ zpszO=n>SQ01WS!TAajQvOy3WTy#p}}jkOxRy=nKbG1opp@ah4I{^PL4w$%>Azowbo$O`$7_J|$!1rYJ?;t0+&X6F zlfe69>CSRd%_pO(Lt_p?UoN2AE?ww>5p^A-9n+iYK$20L@35dok7*|(^^qqv4C zxUPFpW(0WD zI8?5vDBXKTLyTArS_a8I;~hqB<*2D7kDV6fFvX)f@{HM-Bae)R_7Vdn%B~QTm|L?m zM&*!NAidy1C=CVJLR8eeabrLIN+F>LJp_JG1ago=1R_u!#SCg=0TF19q6M`8bqFL$ zI6)owKQz*WB-%*iA9Cs;J?YS9hY)D8(R4#{5@BrmU`*m5DP=Hv=nOY?Z?a$`{f|uX zbEXO2e}W(>HSiF^14)5hX?|q7t#rVl1V1v(AQ5mlwU12ErvN@cQr`jTd}L}AJh4c2voR+EFDN?cGr&3n>5Ht7LI*6-r_C*plkrQ8F?{YBBY_Lg^!PN6M&o zmPj8V`3VKo>0(h#(e$`-=@@HDI9;Sd>FomO6n(3&M0!u@bBZ+FR~&ty=sAVIbX3G1 zE_Y5dDYOb=4;MNoX|O%jtGi2_(?(ue?B07VZhrq|&F{aUc|<95SE8Iil~BZde*=)l zKUBWFV*`+M?w-Qs!v)C+l-niCM@o?sDDD?4rxYP45Vo-~)bit$uI9Vw!sBs@bVRZ7 z*fQe}RA8K3S{y>qh(t$=iq~OiM2gYn#1E)t`uR(UM@swjvf&3641b7H;SW+I{6Wft zlL~_)3I&nkC{{%aje{2f?s#?P1D!lM8t>S|L0}FX0`Xc+zYg z2pESHcMUxZrpc4><1`?O)i2Rv1p@698IhZBGpeoE)_p5j==X zG*+T|56Oc_x2rgHFQNzeq=vX3W5^y+6VS&0`1B&vwDQu8DI%ZM!wXBtmy|xBm~>9_*YbYBV4DM0pqP?J4~C8Ehrh4`}24^a?0vJ^BmUE{tD{>PM!(RNJPIK~GN+A%(; zyB*_$j`2aq_@JS7j1L;^-7!8Wp!|0qA9PIFIHqhIQ#Ot%8^@H5W6H)+CBt@9$vWFn zB|EBQN0scTlJz`zRLMp^ajz*G<7H`M3)0rbW=FYGdn_YxCm`ef!R@-Bjsni z%bt!ypq~i-fU#}RQ!W2o}sb~uQ5xo$>aOC!8~U-H#dGE*pzKVwN~ksi=hMC;%vc7 z7u9oRgHQTt-ilJgl3fmvo84Z*m+g?aoBRG9sqA{VrSnsL%4B6_Ihd*)`{vdA@4k6E z7O!00sbCjb>DYx18bRI;{QC9RkP~S>OYw5t)Dv*iNzsgbp8E(}T1c(g-(GZuv%pl& z*6I#qF>B)TatKaUJ-VT3T?jKoxHnhAXd&)~%9Y9}MY(I7 zH2Bh+QhnU90S@`S;wk$*&y2`ZvQllUeUW)3a#2Dg6u8X%O!bc(I#!fYm9mn56R?$Z zqfSb2tddezqLlm;c7n{egGFR=R!N~}><3YXERJDs19_T8v9P@~E3D`|MSe@n764s3A%J6LnwA&NzacNHL zFBN_*hIbi6`&OddfF>hztV850UwqOovkUirEttwuv(~8G`i-2F!V0!>BXq?#MgK$~ z9VEbuLU5?Zf;Mk6$yu@H3sK30XLbyrqW*=L3 z;g!$s9*_``)zo^i1>j)zkm_C}hEw=(H<_*x4 zSn)8BH(13_E}5*fznGnN0L@x9LNWOpsIlnfBwC@E;%o*`Q-wAxHmU9{hAZaS?PL0 zx053O)j^6%V=>ktdmcC7wDzrwfiBzP%PEw5!rDfonkDum5K_#ZY(V0;af6&CvT;j0 z+`>@NZ16#nr+csPmQ_G4i!gs&2<^o{O81yQ6L?STYChMoI-{!+0=!*4s_DkhQaa3>GAiAf~euH<`m5kHiBIMOv7?G}!7 zH{Po|ILdE1?(V|ofhXW@iaogCB&%7!(#mI)5p7If2L zf0?>hU6_w5;3BDw!b~dWjpwZLGh-KZVyF^2-{lmS@?{q37zZ&F;gTx>EVU zKy&IM%IutYlq&Pb4vO%0Da_Hp+U7i2+s;`mCUl&ljlZfm`1im}j8AoWxLdHW7XL2i zD`%j_!2?_@Pg5`{K!`jT0yCR(^89PDVGI*ik8k@C)wo2T)sTr*yn zWiHK(1|T#*OOa}YJU(u=PG!Ulz0(%r__<27F2e5bZmLIf(y0TI#Mh<0}^APeh zbsh?-VV-7t@Obv!4yuM)!gDl16{^@~W(q=7Wee})VV(6^d+&Bn?hZDp4aSj+3~++L zj&|+fVNAcCU3$Vk2*M}jbXsa$ydns8DGe3$O34%X{Hs^TZ?MlAdRqQuA`yTXdrgk>>!bg z(!Le~t-rNh!^wXumZ1*Xe{*{T7TYZxtBp(5)ZMtaFyMk~00mtx zWg^Yd8W?us?9AIU4gLC&sIaP|u+&Uxg*1`f|hNlxx zru~9pwxuxZIbfq*?824C3-4~dv#ceE)ovQV!*TTQswLws)h1ki4Em6|=GHE3vEMx6 zH~Tzi?*Z8zE^voIrj}s$)>s)}Z^nm~%dSKS&?+jHqCq|5$p?$->X!9ZYKf<6V8H+! zK2Nf3ihat1hyk2ixYDc)0E*j3I5+(})`=4#5ox&~M5cNU5Og+G1I89G(BLWBBP*N0 zj!m-t&UGd5rdab^Vb2e%)cyGCYv6V761D(`aJia|#*sm)p=i!dsnyE_Z$0?lbUJ14 z&Cj|jw}ZL}Ojm%>p32hnVyhvS)!aKq0%vx0&^}i10Y=Z*%reiK=gwL{<8us}X5T7h zFG=j@&jVD?&e+>JQ`dAxEsT0t%Qu`Yl`>2*oS-3V#@QMBm)u+u9X*Er@e=&%*LWLc zVm>)#^U0f1tw3SRSHqe3=zaQHtV#}980Le&DU62ygD6+xU&QX8;4}W`cU8$NvD*F9 z#{7M_=f5#`)%#r`{%OMt|HBWw@MMF_`%iz~h>G7{%=~Wy#BcshtNdy{sb}Lj zs@;6T8a=(5PoS^vcQ>%B`NZ^LFj&7RRizf&<<)$$*i}NGUYwSqI5h|NpXzVVClK>_ zc4iZvFxhE<>@S}hwvcgMy)QYqAXWZej=7*4Cw>2A=VCE`fsiW4kVY56w93_ zSg8giLxmBTc-%qb86mj{6r7+`MSYzlYo4z}I%6pNK&_Awnuc$FFlWh196k#KN9@1n z)79KZM(7$D4{kzp{Dv))4x-X1dsFjOf`MezlCr^q8xb48zJa>_dbI%BOceDKIt{E7;#!H>lDR$nK9bZoN}ATB0d(w+7UJv?FJTnh;Gw0Of#@xr#E?>j7YH& zN5eQ;`0`&%X9GSU|x`oJL1$&`4e?F;W zaQ?@t!&6O3o|~AD z4*OQ7Yyy26d&>6dKBhJ@f8V9*2i!Lg_AJ~WkgQ`>oLQnaXa4VFEWl`MKk$6V>T3e^ zkM@ZJCEI2hjL@vz3Yq*Mm{(!&YFdbdgNj!!WL5HVN6GxbmhjwG4O?zA#9o`_Di!-T z-IG%r`aW#N!&WJlibUnhOk&vsxiPvu&nsjorC5oNOjbU@XjCLUNppR1arx}sc9E(L zm-)Aw>g>tyPX4;(85rt`NR3f@6Q22apg!K=01OwLY-rrm*j!B!A<+beiiq6YwsRcLgFtG|Z3=%?)+ zpUq)fu|eg|^7=C48jbniAb%dnZ=E<6o{Q?ty8Wg#{rSY1ajg(M;90}wW6Hk%@rUbI zui1QZHlMIJ*YCeOwH9Cd2P2J{pMl>)8P;B!K{*Tr#yoK#Y4!G)Np+U0M4u%pHyX4) zQ@5hLmE!Kq7WbQ)`J3V)(Pu#D?AP#r0~l(E8fa%j+oVY6H4K92K;^$HC5X&+WqXN+ z6s_0R7ZK%k^m(P6h~vK4KSgeD$da`-Gy>!Zo`VYLZ4>CN%==TNGL^4H$#Rhx?pAjB z2(ghWJP|}sVu6|Q`(%DrUl{YV`E>S||NW2u8SW>J+}58q3U4M8 zS!KH#5oSJNC!xLT$tX4|xrP+EKHKLi7)lc1#)^abeQB>m@-B3RK)XwMQVX_V45Ixu zDT0ul{8+6=%reg5r3ZuesHMP&#(;Y(Ur1Wlu?#p`=G$Qlb}y%dIIHBz7_CznS)Gtr zy|Jq(AyhYWp75?pfP=nVR>u{zN)uK-M!+8i3_CDf)!&lC`tRLZCTlDnA9QG;wt31+ zz?yj3S6F_Su;~_rih+d6X#Lgh5D;Vj>3zPhDc%vCCHZ{z0liF~yjohY!l% z4k~L%I?Q7{s1-tM%L&3JLE#0<clCM=m(qAW^LiL@^MneCd0F4pol zV)$u9L&EC)(Rn;ue?)cKMJ|o72QywA6J*u6=rnErWzK}ko5|M3*^ z7N+t9!e=2ld|0_P4c>S%W3Q^m7(i2sL~S-0Q&OQEd1Kb^hHdi~NoNpt-K_W3d(2_k z7phug{Yx{A?+}{YtDZzz-CyB`2K3nH0%hR;se=_^62^co7I#(ytAo+u%WUW2u!gSzb$2G0zA9;O z_C0P0oYrk=_5}FiKwlhQB<@0rH?~q~;26_e2q9Z;Gqcw+m9H2d-}-HKfzc0|Nr)$} zB+s(ltQk#3xdm(1f%WVSZ%!&JCD=PL@uRWw%w~8y?d%yeoa3~s2yRiwzGqy&h&z%E$8(dh9N!Pv~YUuxIro zxS-Nug+CA@92&6s@xNO;z?Jw|h*H7<&Hl%bkU=c~OD_9LD)veX3(z%(r3(}xTW)Dg z+)=a;Q=`jfYyZ{)z^gVKTwMk@ybp#Y;s%$pyrU**gL3UQxMJVieK<(-H5|#-C3O;R zf+t;f*6G|(+ro8k-keX= zyvLmVgIFIUe)S>R)0UUE^AYb(>i8=$m$*Yb>2uR}ch~iz$xA!GzV{=>bzT9UA^uRQ zlCMPg%XZ=K7x!Ts>BWBj#8bBgs0u{O-A{~ z`T6iqKg`Q`bq*m>+=4ryCm z@$y&apZu-%;QmgU**kK{WJr&|??ygbZ1Xe|(;C3@eD>w+V&tV{>GjS1gpT;hmp< zb_sTDVzqib3&uGE2wss+7rP0YnR#U_;|r158CZ*KGt+DH1#^@A{4<-qH2<7|!2ZiG z)A|>?671huDT<6If=zxiVUxd1kgCzR;ZQ?|kv~t1jOSu%SjNvk&)y0nt%;d;q`-R) zBlx|82=-hDD?|n-zuysVBv~^cbgB<}%=4NDCcF|WReCr5lm#mqa7mAiZHK9=e`)@1 zG^Pe4tvij3TIPVy5&Xe$)kGPd@24Yu$$4OeR1~E!GaKGr?nkNeKk_mM zr4xd?F96s1(v8~{c6-}Q>8)u7IoEZ9|HhnOwB=%n9aiF2b4nuwlfS{Awgn{78DabO zVj(Jifr1_m%XJ8sEm?~VcQ>mlM1K9|)lbjfHGb|W2a3IS?jOdTYX=jR!(z!UM9n41 z266)GfrX8Etr2}Zyz*j<-BXw-Tel|Yv~AnAZQHhOd!=oxw7Jr@ZDXZvJF{}{Q+@vK zuIhUFE@DQ+Wjr(Hi1B{&*fmgDqKK#IBqWdQ!+;QlxE75~k{jW>L)Zi;bQ>W9OZj89Drk$I5F%eQaI3j2TrnWR_ma%Fw#4r3`{OM;`)45@ECV7d=> zd(Fk=AN)6sa2reon)F)l`S^k@V9U0lqsDDg{lC$FgErsH&VK|~oI63-PXBb}y|fCW zZ6)138w+Go?+9_WLhlaJ!1B-0wH;ovE4YoEt>LPH{(dG9t<(czHbf9LF$T=`BD-r; zU}}NX%-C#MIlnX_rp6^om|2yEPYt4=Q*bJa?exC^1wbEMl%cVO6zVJfyx}uT==%0r zKG!8o99ES4m6})B5%Qzb%REHCDlmm-5aW^q!Fv6ZLhu4do==9`*&i&m2+jqu^bY$h!^zmua}vSs}K&AQWTy? z3W;Z&pO8ZY2Z@Zy32xT;s5y{g*jdgvZBF!R^AjuHX^0ZKtx5)FxV@c zL-gg$jKcJO6x$G39v3y7Ts@4|g;#8{n=C+)ew`zfayP`vYQcIF`dn2|7g^`sOLag_iJdW zyA@SNqSlZdL1vfaWgrf1?CPifOihcD9Gg>-xW!}pv1sadj`5&8A0B+`>IC>7=GQuQ zs1t^zUDJFTo1UvZz zlk^L^#I6N@t3Ha#qiC+~Lk&gnDZ8V^zu|p#EZ%G$HN}9Tu*-z1k6fB??%}5Hj&0MhYkb&8><0U|BGm?~>M%WRJiJkJu7EH!ahw>Qx`V@} z_-pli8i%FeaE$h{X`mTyVtSpb+QqXwUsP?2x}OJ}-WLi5cq<0uUY6coqAR!x(@;s zSE!H+@^S-Aa&ka7s53SM)vD5=SBu@57W+*zT+deStSnMX%&ViiSyE8EN*Rm zL7FE?8e1T(ZyBiH26nUB+Q;^Tfnvdu4^W}bIfVmr$4BcS0|X=kTApQ-*m7F~XUI$6vrkB%ajoU6Q(vj9B^e>azbBjtuy@$CpdR!p)Z6lT2d2*#EWLgqG=lf?rdnjSuY)@;)y;7mPv=uH?b{7 zW85E;VCVyaK0_O%*x&GlTdSBZkkTL%8NOy1kKPP=bZ6CU~A@h#fBOIo(;L7wQo z!tJq)APOQJBAr2mt;UM-qPje5!-#H@Q`MtF@Ob*`SiP{?$**p;D{sAN1kc8VwQ-iz z&s7;lW{4A6nkKqroVn_5>seiB8IEoqIhx<)F=KT1j*nZICb~R^ETuL)_Eo_)@?@Iv zv~INsSLo|G3qklpPwdCVEdbv#D{nK+j0^U>guBbpkx_Q0HV-V!RRu8(fB zMTlR%_|mSg$O7+IHfx{OR?n-F%ov{Kel;EUQr)hpsTX|MdmIY2cdohb?4C2GF}QU6 zfdQrx%~FbiMIQvG{@#+?b%0?oM4;I<$O{Prk>r3N02Dy*lEedw4WM3^X2QZS=F zz(_@bc<1az5DV506F>+DfB+A|?*)09z9Q@e2XO$F+OubwoD2*AE3{??F}Xn?0L$$x zgk%=SdLS_9>oWlDUGxIShgU%eKzwi>0TzIo^;?j>V+aW9`&|_LYgjMx8D7SLf}&4= zvI`gx5{S{sVoOpJ8NNTR01#rBuoCQLcsCFDLe*bv0WbjgbDgIdxEBm70H`aZTmYEY z00=nD0>;DuK_V~L4!*aSBV6Dc#U&7W5CGyq$qoQoBnbS$y#M?ruYv_6diktC0Pt1kD7LO?)%e!tfY7XMEh+Is9Qk7;=^ZlS6P)}Jp2!g=bCy3u z%2$GkpX}l}F&$hu#>-rKTVa5zjU!PgbH*<-OqC8ZvhSLiUOBgUJv4k?=xDn0iUA=-fBe zO2g})G0@zZm2Bx%>1i=qgWcgf`UCm5G(x-QkG=&7m8K9xG5Qe(oDQ0ITiAMrSI;5S zMryzHEsR7-0fKN7pbFf|Q|K`_+EW7|Xy3=%2m>4Lr>^kk#Zn^gq2|-#{``c`HF9b! zd@>$`M!(ohBUAL|JDrwv(CU1CH$(Df{WCFD5cKF!FZMZl{{9F zOZsLdl?|qrgnc`BIEnDi`d3f{61cmg>_wpaY(~xH9BK3#<2a#jKd+{$0Sz=d^%m24 zK;evfqE76kJOKOHb}HRL>ue>>xV$T#=%MJmZx^^|8yjv|+@eqxinOYBWnSk1IKSH! z+L_ht_)1sSfJuuIXr?YK?EusQ865;5JX~x8{3l<7e62Srgqwy?2QIx|`Uza4PleFp zwaCGDc+29O<(tpYx!Ejwf}*66&`$9Vmr6HCezHh8y`WfVPev8w?vB{`hRXwkb8}YC zPeiV1tuus|zQ5Gym+vmZos5vvX5)khX=(Idiz)+u`XV1%zgFr zq~bGaLlAKO`5S zn;4PskhRy+F%^h+x&{n8_`1} z#(+qSvOU5GJ0(+gwC*DR8zKB^!zw1fJ|w7evdsNw@d9DV@||hs1UwX7C1Gxl&~*g5 zSlQ`!ZmW5mwbMOG`=c(~+J(U1TN6_|;MSug0xFtOh2QdE zXQscZ8L0`_yS{F_#9bKG&4X$|%i%>y=9(sx26MwWiJQ}X`zcft`j=NJLT-48){Zh8 zz681Z*$wa|C45p~#4(#^R0}9<2BE54v8i=Ynum`tW2fI! z9qe1pT41wYHk^%?*F`N7TVR?{#P@NnVE5X$v>^*>MgMGQSQgZ>F59y7UK7R*0xsZk zGVd>`oU@6B3V6-iIL`+Izf>C%FG^Hrp$>NgtNg|n7)LVg)VXPnk_l%7;m~cAhA$|) zJ!{ybn7>C*LK_f|TMl)OiH5z<`hVh{5ED0?%?Q7KIRM$%$%|fy@4+$ozgD!V>11S8 zg1!s|w)O^~8pd-XjRRFKaSZ>;9;aG8^N!aGZ|FglAmGc?DN{}}P?nrZ#+TN9 zOpo~?6XU|SH8wJXFI&gIutzCsr=?#P-K58wZmyq)t+NZx+&uv1Y898s)zh4G>aT@NfzKpN!kwmfEzsedwsn+Q2O(4 zKeMlQ`@kclK5GN{dVbLLsUaUHEGzDrqa=Q1RifKxv(x8fm*UzhDo z#;eo-+8xxUhY2A!H&6X3u5PVE+*KL9<=?EtdDO3|hVf5azMooBtr@M}D*gBZfc~x3 zKL3E7e>(BA{`EY(cV~*XKf34N&ir?`aP50=&D1joce3v8<6#ng8CrHSg$2Q;@)kNj zY@>qm=lU*=vy&8+6R2J<)!4kJ|B^fqJU@~r-X)A~8@2cY!EcdE`H%sZI-Cqn(KJ*+ zq$5k#J`fez64&AyZM<*x|B^f}|4N<%;Zffo$-}p}5BXn`XO7in;0bSbMn_ehgvP0> z39aPypn8_712rBUIluRHk`f)-lDL!ckAPK}yPLAQbTKRRuQ-XXfQ8Ov>Wi91z7tOp z?#``dPpWYna7i z!x7wK8Z&}qh4`Csvnbt{>_Bul@i0?m9>DWi^LTi^t_HMR%v*6p8sI}`j@o}X4>5V@e>l(k|H^sx-Tyy1kD$wc za~_3>|KdCVw*TQgi(0`w|8O1(z5j3?RwXwJEQP3~D;K8A!}D|eZbSVZKX3>e(~5JK zT$#U6CCw?RmYbXFmaYy1@vYYYw^O`?Y0?=s4ID|tc`I2H>v{}FHK>PR4!uDrS*@== z@BnccY77R{f!5t1a}$RMV$&2ag!*3(QxA6^cSc^XLuoTJh#I{q^t9{_pCXw~8=U3W z-S4;X+y1bcq;xHGn^=mCmtafp94#0B&3OWZ2W|d~^8~s5!+B)>e{&v+z8}tm^AG3A z1AQMo{fG0+I{*vwf}s4vc@z+SI8W&h=P}0lm-B?^|L-`D5Z`||k6_*p=TZG-hIjK1 z=gBW~jDrv2$}idVaA&ph3bp>u@8rPhZLP!|R5v1T?o$F#oW%vw_utI$-Pf@VO{UG! zpd7t{uaMQi>7a;&%U;4S=MD?FfL9G^iV4)Ix0kD}jZ?xmFtd!?s5hm~3iB_(UQeg> z`UmqU{EK;j`2Y8qC-T2B4~?Zdv0%zSm`DEy^AO7ZFPI0;aiR;goJLg0$S6COQ+%ge z$GO>|#*TbuXfS%!LFXd9;hfLoD1V@ZXHtK;W?4&JTz#K`)fE`JF2&C$9!Mj~3{(b#RPTooa%!g$yz9g$ni>s5zx8a_5-&2A283gOM9 zqp9+@>N!?zSeg$p0LI80^{6$(=@2*ck51moiT5mEgMf^2Q0fxx@`@1R1QkY#Q@_%s zj2L%Swd=DMFKu(re3JilNv)gQ*cBY>$eZ%!V9w7yd#M;nv>)-}mSt*B#0I<(jBYz` z3Du^^1b4?^@CDHf@g$KKI~TV>6I>pp+TV7}f`&2v0RXrQ0sSj&Brr5S>VD!FzS?V5 zQ-DE(g2b=YcK;Hu+H0&pbiR(1I&{an0~;tWu38I4vZr5`9}~V$|2V0kSjik=56_&Z z?`)N0zWrIko=?zclp+#Z@t*Ml^Wh8`hDWs{wFBJ0Nyi<SM{q0h8rITXoFT%&jV) z>d|m_Jf`}Lt9oq;Q=f#r^yS-}-7%7KM)}={DwQ3rJ=r`%dvrjq>U_R`h$MaO7rydy zjLr}!qrce~6g}>w;`TMv9Dq$tl$_{a)u+`oW;b1R!679cG@STS6%^)`JibQSF0Duz zhs(#1A5z7m9C1M2>104+IC2vrH6_x$HE1ilHXLj_$a@=Udk^2%9>tupMkmAz5l*?M zO-q0iea;H=2_L3Bq#+v8xwKK`G+E46J+>t4$ z{ktm$dd7%CY3qZqg-$U+sPRK~@s;hJ_^4NQ!{~_c8jNUWfGODn8wK(=(}lc% z1vVgRq%N&xe4vSp+WbOg6|%yT#*T?2dUv9UlHQe7gQZ-zO2^`}@M$%F^NZ{l(35KB zze`6+uBxc_kRU9XS6@$?WKx1odY|A-L&r0@Ymp+}z9!{J81WrPh+~(Xy!NGf>mheC zg12N$vg(qE=ieo>v(U?fT#kiP1genZfGeLz(ywxnI){>4u7#bDe$@3 zZe9eKO?CNccGvOit&dKI6GeIaiD@tx0cj@4-K}?nthFc}OzK+_h276zX;q7nQW&+j zc5&^rZ}uM4J8W8dZg@VS9~N`UG*@=^lZS?Pwm)%W@K*au(`Vss!*LH_qn<*By!+yF zal+Gf{*XGDS2mNaJ5dr%4I5VTi(fP{ zOC|VScHAgODP8adB zP(E3>JzOqgoQA83mz=4+bhEbG* zv1)xR`_JF%#BRvyZ=9Gr_pO(80@xDoK>vT#Sw1{b(&7S(|Z zz4RMRz9)8Z-#q0La(8!sgV>sig)kBasmpN_LEt6|$3-0c_nh(h1yK-Y1F`)bV#_z= z>gQ(s{EoQUE%B49|D#nDa#vP1;8M~zp}MOWW_4VnKS?2pX;g|v7j4d}v-Z9u_!@r9 zQpcDup;MFy-W@c^C)w9BhRUAY{f8N}MTyzctB)p%pKGq34~ z^W#F z47VDTi1?Nu$3_f-8ZV3}iGo#Re;<3taCn4Z3`Bnk*&O0wJh5RUei5A8UCjK&+CIwj zWRq}vH_^=@AC&92baosyw9{<<&d)DeA?*E8}+@WKrf7J2*YTMg;mt4 zR&X$*ghEp})D1pm+U$2xFk#?fz*BZw95msTjQ#kdz=cshL#j2zz{<6OcY&Qfh!(>H z$5C_|ZkE|J+}#qe4Me*$2zJ+yEUtc8LGFIppRqSRuJl5@-I7=bW%1T>;>~3H?~I)i zBD_+!tVP{B_c1%8Oy_h3o_H-fhGqeS{c*gEoczRqPtz~4@K8|zc*+ZOL+BTuq4)4( z`%VFU70K>0BA=StzpDuZDO=Vl^Vs>EN)~@wJpMwg)i(a+O_?Y}8?{F7!Y15MN3yS- z+=7^Dhxq%6+8jk=s-9Mgy#X*VbUq9gT910Ab^&>1fuB1iU>JcU#Wgt$e6_)i~Y*cJ-NXM?qCo$@3 zH7YeQ0xjGr>%Q&`jJ-K2@P_%?2rzJO7=VuAA?zfEPTP>p%)G$R@xt8QH4rg?RKF=&iB;M&d->JNIXQT8*jXT&9nA zoy@|lG2^G8YCFRs3>+m9lc*Q(q$K6e$jgd<%}J80j>Q}q@gds%M_mewGk#oe-muH# z76Oa^wCB%)&g>EnL3Ym}#7Y3m2WMWP@51UPflm+Hahg!jzIe!lY`I5_Q{N(jRL&PL z!0W$&-;6{#Td=@!eqh81$7aon&BH>NF$eY8Osbd(3%`4FZ!^xtl70exAUBMObP^P z|LF)wxv^M2VF~25b-PpwU&PTojsq(S`VYMPwfX}uF)}9s6a}oJRqR3n-|68Zq^7Fi zWU>tYftM11H=2HiDxUO`-MN7b(8gQO!C}D?uRlL)5FfgB@2h7I^%4>Av#1*#?b;N% zLuTnXeC;7ZZ4H4=XWjO8CzrS@6S4Dm>n$`*ybDs^+0`wwGmqo1cT*wF9*qeTp^i}s z_Sr%I91xvv0~y#O@;I(>?h%(+(&ey(e=)1$vBJ$ioi2*GfGjtN*rJ&Q_nX$!!^33L zFi5Xg0&_0lcov2GQ^-Tm8fXStaq$ZBV4CVw38?rIy&2C_WongdIl~GMm zy*D7y$ve0<`=%VlKiXHauo{8UJ7;+tW_unT1c=Ahyu=n=To<1NU(XzgzXBy-DA>If zP%EC)86asubl@@@l9BHC(fN^uQb`PZt z9jSCec*~EG)uQmT;Eo}oaMpj%pTY;D=mvhpu71E`R++ytEIjAK9D;ww4+}4`ZDRXw z1O;;W*MlkoPxh457WWkofG1H|Q6``>=}%=w-~PneCA51^!6-BZWp-GcMfLa(TLgv^ zL8QepqJ|nS8`zF!P%%gq_)?6lIaX53Y5$gYh`yK1cI!9d@z#6Hm1paizi^aSI9euV z&jqpLmv&E$vf{&B!o+q)Fv|s)e3&nh1{`olzfSMRIe?78d=Dda@xq|$lb(Qk=tc6` zRO!Mw)(92%u(*ol=;y8hl73;CzXL> z)G;)yroVVU2-dYOEeG0+|9m(52L&nMRahWRJRp^u!3k(dvGtA(hBNSpLctSyB1<33bM#i3CY-nUiYoe10j5;ug<)zV^(zwmY%E+@_? zY8Z&57YiOTA4sAE!ML?z3CdW=l5$}61;+lOLz&{czC#4QzEH#Hj!JMNj|D=O;(->( z4LqYaYpgVEfoBj7ZsY`PiZ2(TlV3OrtB3@vglsH6?ckR%ajnmFTL{2s3S6s&<42ISZ7S^r|tWvhQ#-_LRW zkMtAsc2^k#8ux#u()_Ir>UaR>P`VSvTWiHqX6jN1fO{=E$BbTTS{Mrg)*}$lGKY*? z#7saD2CXzF*LI*vd~Ap_u(l0$t6=O>XR*6~Y)tyfb=MvHBp%L%9R)~e5VG*Al5n0& zYl*ghavUp$1)eE@Ld4A8c~sKc1j<07mv)9^xOl^XI+8`Fq5vXE-o^vD;xl5rQpprk zglRbWaRej11?~KyTI1{|&PuC+Q81iJuqHxJys1Tj!eBCB($LToR0csZxGCXtHQzQT zl4%1ozX=n~}Zax(@m}A~DTVN$5acWKZIMrJ!&l>r4XR!??f=1cB%&_tPiy}0z z!aa~XEzZd5M0@XVO5wwTH-&6m>L1ldv1DvbRe+aEr_K%R02aUULN)-&V>2Nm6j}zi z+{$4;1g-lN^?JMhu`WV|pS(`1aC3;|ie$ss7ADkmx!8z!kjQxSYo;Q!j8&I=)7Gdn zA08k>Y;0kv_mY(6-Pf8Fd-iVJeH2S03d|^gud>`YXnta-->@cs<`7tj=IC1Ea~N2N zhRO_B-7u0*j$yToZ|C`FJz|FXqZa8!d!rFnY*{R70NI5|)ovQemnGjf5t)~B`C)j$N=8qpjL zDG}yojT5vzBuxnvt`O*i8elI%!M1pLD~J~+uu~*fK---(KnwKt?LusT68ybiK5F}c z20Denc&%uFF#G!XfP<}KppkuI&nXDmKqlAVtZRkMoZE%UeZ?C1J8BMp{MwECQqLDI z2SJX1ib2LNoaJ{Kd42EoZwu!QFzRNGE_;y;tM6MK?0x;uUHu`O*vAb|N&(fGq69EN zs2YZHSz8T_b~4|e;9_;c86)sRs>xV{%5bga_)M7R%xazyt(}%Bx?ooAD4hq`fDvcY zPSB_F$uHn>;r8z6I|!pbhp9-Ptlc}nh|GBGzE>SdWjgAIY~}6lH-y~FwyYYSeBzv`YED!xGi0g>e zB(P$I3Xeigkc37+T+m6g-V?Jd(h~?{Pbhh(v{b_EOQDY21BWKzEawHxAwo=xny!at z!_uR(==ji6o6ffwUTf*XY^wf!>1v1=RNm*bs0*53za$=Ki5PPOOXsBfMNopvp*s=C zEoxz8)Gg89O6<@BPHE4CT3jAi0=B4+g`TY7R02Okgy`TE`+uo*g6>nDICJSVA0|oF z%|_MYsQDf4^iwN}vkngnP&FVS8cHGuXh;5Pn)9sGsQ$y&sMs%)@Tlqpqf%2VFYC-T zUmptj(h*}!uHXXJ*e>_>^R}ZE(6?mF2I#ybcWJ%JBa{WnimW-@;rpBWM)FL!+3;zqt1{*a+{C?(+RxZR@Q_IM;)j=5Jbk=2XyPWW zo~XyiC-dzuBdWU?6o>}M-xW35AWd#$;TjO4I_p8Ds zCPcu)vfh?}F!|KjjmJ~VD6!z1Pun!OmAz76mQiwi7|W*|7M zR*XxFy|#=C!SWs{`g95llIWc|6h1A`hNyM(TKEQ(u!1Db^K=5$=zR?EB&tkb5|1n8 zW8*_FQU=GNNv|qpq|VlQzxK*OZg;{PaTct({wQaG$xPGK@S*fa&N0@lO=TH{RnWFE@VATWJxjJUYUtpUdV>E|Ax% zsNqZ{A5-J(ad()PMPM#`0B`5@U>otnt_8OcKAs~R#ASHH+*T|Hw)pQD;^|=p?me66 zr>pP=M1`-8ku{E=d-CDqX2Zl!27aDFe7h-2H2hKP*_UOB|ITzj)0-C_PlZWgZmasq z!0qf&Y`ob&;YP~FnM+}PCi*CAb~a)9+Ga7H?JM?rhBAR|b_AW6s|LeO!y=#=$WY~J zLLmfH8uCB%dz4<4vL?Mc(5i<~sr{#Ph;&A=jw7Yrih48VQCRe4G;)*_ZltaOg7i@h zD$jS5!eRPPsX$0eJfr3oB#<{8^b-8+OZj);icL^ITiT%0+UEhrBBnH zvVMccA6u9FaeL7=pa-Fhca?0$G@{h`r#(~es6cgp`Alp=s7L4g)r!gUQ&&>y=b4+y zX-d@8QctlM7szl~JF)(}BQCrQCoG*VRhhr$?4iVeK^*+hh#dcxDR7^i1{+n!1RXbs zkysrck!CZ9H>08lf@4ZiX0^3&Bo-#%hROo3Tv66xql}9dhpZrw8kY(({{0QysfnPm zBx7qihn6iQB;gNVxpxL%PmyQjSwr2ouzglSW)T2fAxLisWpaJALq&1Kk~0+eZhez0i~u~dXGF^XsVH3vk6|L+w@wreFOLj zCQ*MLB7m^f-lN~s`&oq_5ZH-bKvDf)W)I^iX62KSv$G2QPbd$PE;<$d#8TMO{SAwp zi!8S2Sn(p{zJ*^iw`aiMUZucU$hDqMC66q7Lrn0q@*aa2;+a$u!;(< zHG`=HG?iK9-Je~in4Nrmt5-rY^@6<;Q;R1J>jjlsswj|iIDfBdjb7gREE2svX-J{V z#{1rHXHwCTi%sR@IdAk1L!iTKrFL@lyuB@%mWptqFX6VRrNF@VYsMbMNl~|>Rd z+V`9#W879D#WX179caO=LbmaC*GPkH(S;Rm$RM*jjveVr&xaP%FVp{K_sHJEY}<85 z7#C`lFIB zsW&Cw@M=MG?ahuNo^EKyqmKpKYo}TFOVRPnI&bB-`9Z-Be)FE+Gi@p3E3>R6T3rx| zkMiZPn>vj#Xf@6g(oXVYa`MUNOjHQEWXgxKy?1?{kn1hjw)k^BQp+q+zOjaZWHX{I za*tekE102(oW@memhn(MPAcrwJxjz1QOc7PCmJ$R9mZZ@{rYbZr*UdjxzJW{GdLGg zhlEM-KYX)gnQ=vm(GqmG?mReF_&ZNhmUT{-{$q&KX(a9WpfL zR!?w|QDuf{l31y!+owyF;QSJfI15iK*sMU~jw}iJS6>a*0%u=B48Smac?zU18LD)tF6UPof<|0aOuC8L} z*|#zoeKYc*{U#oK1ogiohq8*K6tA5nRLL1`qy!ZjH$@PsCl1METSC-OL4zfph)WgZ z^SI~MjT91^GiPgzQAK6Ri&ut!XlXU@k6Q7xA|yVh*4L$`gL_4sfv=kC>Kd zTyi8sA|X<`oZ&pl8%!yGO21qJMD}NoQ3jJJmU?2)AxyYj8L#NF`p9GrJ^=hK%mp$=%~x#9ybM zp8*R-n6etbRXPJ~)oF;E&P$A-*R}1j)OyPds6k>V%CaM8$8Sd{6S|!-__352GUBl_ zhsp80O9VP()=3@QYC1oH z(!Gf_;c4y^6}wTeJXoHTJfn5;t+s83O~?(3t-Hc$lz>Lhz{2i|pFNnQYV4w)>0ey9ZPlT60F^LHY2xKm=#Tq~*4Gl`7H zmZFy9{-{8((!iN%QgE}E1&b|GCo)-w^7m{92ggdaevUQ%P~xr6D7U$3aEeueK?p{m@#0FoLP?yYcKz9zy*duFF`{U3I$d`yvVX(V^#Vd!NU% z4R!S^sY_(1r>%{aKhr)1-5sr5-&dF1Gc7fA@-sfy9UNU;?_37=9x1=@z0M=5SGLhn z1j)ZB+P#`|9h%_+Y+HA$0z%NcT)G=0Q^&2KUCXkd9v=aW$30dp*yf85?}E0;>aK4f zp1niX8rKV8bD~BVtys|zWB28x-E5ke2k&q!7Wi=bY|=ehPMJT1Y0B}21@cgf(PO=p z6WyFAo{IY#+0&x8L(ZVHVDN$j7C`!o(Hryl+^u{&KJ4!sjsWdI4Kny8#ozx7@GkO2 zto!30<%d4KeKeL>l|7+o*PH=9^)i4rV?K?z7_pn(^mL$en*f;#1ES74m=ZVZ_LEzP zrO&$hj!G7JH@9HFd%O)jyasdEzsC;$+0L>h=6kCHS%VRz+FnaldqxM3uz5>9Z^P&p zSV!Tc*K8-H)DC_0O_B@^WTfo-L?M^f+~O}mCH0hI&IdZFepJj$q9~0bkwr**VC^w( zK}ZL~e{Kj8_2~jLeWJl3{2P)XZJ>n=HOk`v|6cA1dAPX4FY}!eMKOD+7Skt6|J2RP zeIYzego!7Bt~qX*KHv+e!2&5%xf)hM;-ehVv|ZK=its(F`|V2!hg%tfdUjEfCmqgC zksPB{D(F#g-^m{U|C7;)0=hSaLN956@2XC>CD~pVqnm#^R4S#KA}tf zo3QI`Sq}#Hqp#+(H>A7E`rE(2x<|c!;hMX9_p0L_b^7))af7g~`bpV#z%b$umJQ1~ zih)(s_SvO-8?@qiHz=`Fv*}aj1>8*&mus8I7FyLomJo&-c$O3Jqj@o_W-})OE32mU zu678UG#baRF;!70ZY9Fq6;R|-T#_MXtgX>u!{}kSE{T)c!KuBLbvR~|>hX%8(sH^9 z5$1NKaNP?YooP#)fi8Hu5~jK)QIhBl+~!g;oa-i1Wt_FuSsJzHBw4(L0v>6eM)a6Yt<#hP05E@)cX0E;C4z*PC=k`pGSDz~!yAHXU^&abPF%VzM zxYlwRR~MNzfgwB;kvyc5QNGc)%d6zeE+i&70fz|+KO`mIbW9$QVxV*dYpJBo59glB zAg9dV6(tAzkEOaj)NGV3g3c#L0Vux}i3^i`on(LVjTs5dar-irgr%aPoOf>0q)8?V zFo7R<2P9DqxS#2}jyDwr#=&DC+hb(Jsf2#|n20G2K``c)Y>e)ZjG4HFQUcjt%WY;* zh2cZ!eZkda%4H-%iabHUcXnh|tI(FUYjq~O@wcllCC)~MQ!AO8Lsp)S;NW1BSA>Ig zTrzj99%Hrvp@DGpUb1j$)hofo(TpH0D*f!7)UIkCnBIWF*hv}lH`sx>xh3xR^W5v&#q+gaQX9i+_>{W2W1 z1TLFn8DWF6PB8}m3BLSgfP@~?3@RH3xgnB_60gQ6Ur{AKHkohUuE{EEM+t94nJGht z;Z0F(XxMWia90VZy_2Bch9ALCpbM(D7g9A=H+7hAI-^`AN9mZkK7_4qD+ z@Mjy)c&@AjX=MpvmQZ>rwN0$|&_vQ>A4HzDDO9HwW?Kin|IPqvF}%1)!E4EX8oF>749IaWj@-A6hkUJzGZ2)y4-C zzM$rcp32yt3k9&#Iy4k}J*zbGM}EMt@Tb4%XIG;7+xD{MDw@-Z8KC%{-j1&+Gg;=FMj^MJFBn*&9(S4S zpCD7x?KDmJ%nsYi{tXAne!8}>aVG#i{;xgLH0#n8iF`IQ#-rv|N_jUh+vgFtC6c-8 zas>m%>JyjZp8XB3R9Qmxr)-8qP10cNES=~Aj*P^rs$SP??bwOsPyr*B9In$;99(R# z=B=bddwjp&S8gjkuwM*z52S$e>s8fKs#DyAX5K)FEqr-S&p0`(x#PyiC3w z$H{*mz%hVVEVAXg-IvoowX2K$$**f4)kDQ*6SL#aElGm$zsiRT?2a3h-s@*kAH@vaXVi-Hsph>LA8(MaXf0cvHlq9=5BpaG9TZkU9c`owiN(~{i zW5?q|5RCO`mPwL0+`TI((EJ5eY(V`KNRm==dYf4U?x==`i_7odqB-azn{@Hmp2_>U z(8tfl0t)`6-EQ`0CFekRL*-5cFea9xl^nOtqNG^g?_*<-BJDJ8Cg*GH?cx-;EE8dU za4RZie`0>t)dv_}VFcZQle7EAfhSh?>6&Xy#m7nECJ6@WmCJjXebV|v&mQ+?6IVaw z5xaAA2QE`mDjjHw;0C$Cy2ZO7v-hu=%1uo#1OP|n>?xdh52e8ptu{E{`Bt(S@J#aY zBTX?W*S&tepPNESOfuAr25XXGPecO!X=zP>t~5qp>?jr=7yWVN_21t)J)&Hm_iSrHA}2W1ug4$ND#-{FV5U_yILDa^NcR9T#r5(%RI)=dZ)qcLw*E~8Zxv%Qq! zfFjayG~Fv>cY{yR!j=@og2p;uVa)79Rd^?3(8_Q$R=FeU zeekKhL%Yx5WH@SgHF;a`|Ge7vd%u>4%i(*s`52*>ZwE~HyHBjXTT8HI6lA{qq((Av zBn-)j+Cu1_4z#>sH;URdrb{?cV~VkdtApy3)Bz(7dy`xtT!z@TRdVf5&sKat(v0^N z<&k!W*B|BDiAo3xm0LF^)1JG4ksimT&5CbGVK4>-Fs1K^`sFMYQs7XEeq8Sht}qdk z%0y_u=S9vUH+V!0V_Ol*@7jXwUBkq_8pb1AP$bn-61w3d0%zmDqHKS9r`4QuUV^Ao zJ8Uzv{dH`NmIukSJ9q4yk&in9M>>v=`j*Eg6V;9? z7cx;br0;3wjDWd-E_JTlm+h*kaIRtc4fhH%Z9zEeu$H)fLh>u)kn(H?r$P) z=J}aSFxnTgC`&+2LG!(ah}X%T#os}q4FFVNXcd#of*WH>i!8FQ*)%ERD^*TNsYuFZ zwP;Q1FN}_4eKEoEmmE&vbj&L7rK?1ct#vqgX%*Z4loo2`EbcP|1c0tchyf1{{Y(%y z7)9MmX^Otd*-DQv7bR)m6A#+Q89%)CXh9Gbf|j1D>(`tIAY>|}1eI2m#^G2H$Dd?q z>M<&{1JD5)&=iTM9S~T9i9FcA*Ni8W`ii0W%dQ7IBrZezz`C)0N~9d3E*BjLvODD@I>6V`S2R zs{3_RF|HNLFE&)Jz0W(z!~q;L3)OL;5m=^B*(IdAp3@2R8vXS{J9x?RV_UHFm_CBt5>+uY~yEgHQ z=y!E5I5b5}C@edLP19y8qavxI8r+Q>Hr{+Hpu=AZawCDTNvx zF!BTKHn?o2?fz6793}84B9C=9v$nVZFWrn<<_X_2ETuF(^VALH;%R1Ivt6+zDG)nH zkDGMhNn0r^m!EkKZ@;Vf(I?2|(y+l^M(P}D{U#vhGZo8Z8i0q|6AyegQQZ4)x~BVC`?9#O z-(f$Sf>`sw^EZc`7ot$Zw|c$@eNLMg%j;~Bde4W?H~Wc(C28@&PLkR&%D4AFTu!N0 zmz~cqw_HP|FBYnC_XzDAL#hPy@vC&^QM(DHvZSDzPD}~q3P{yc$-Ldu=SHR}=vF)x z1UTGpoLdKSElU_-wtLPoI@T&iAUTKA%dZ_*OvjF15m= zlqT8V?r#)=R#Mm{GqoF9SG12hr)KN@{=WIpy_}&nu`EMNE{#<<{59~qUJO@K0J+SE z%v$PpG?hkDd!VpD8kn|O%QlYi*tDwr($lbkWtHpLHBs$ML-WK-By)OVGD|i`UyX*o z^k9a?Yo@-{h7u!O08rUVs%dT!ETmgIvz<3n^8Nr^Gp@9`-CFqLe*n8cM88DL0)DHO zFE0!1h*JV)NGBg@taclgSs2ur+yYbbPON_u<&SuF%L;TEt+M2wTDI}2(`uK`7nZyj zL@)@^kGrN@nK?){nI%uzjBj|sNRlnNsgJNqDjEKqMSQ*9WJ$H1ftE@Zn~E3ZEM~VX zot4RILW}4osaRBP3O1v8GJ&RNm@Tx9f1`KolNPnHqmXyQb@drgQ8%j4HVCNI1Fdk# zm#^Mk8I!2R0K{>((DY^s#DGD-SHd>bNy@S~=Se29^J%i{S}8Y+byAgf6|E^b(|PTf za38}p$bcXpdjh~)G!X+EHU&zZo%q!}UUFRI5;==%TP0>?0_N0UY6-2eedH2aiR%VX zaJllOmQkxC+T|#;3EYrfo||G+h8vSw8dzyJsmj{UY(vSt>kI_D4r%%zassz9i5z$M zv*mqYr;T?vNyT~ejTxn2*KimP25 z(IO@(U9glKTtAy$KSk(l{i8OK(uvL}R9#G}lBG-SZ~%4<_^F{%)C(&qol~{g;tgI1 zQVt4G)>a|L%G8BluzKz_sCl>swpz2g6)FwGtW4xYY8E8>ShRicH>2ua2)0e3@@E)rU*Sgmt({#A5wbNDg2K@3Q3M3vrBs;EU~yK-tSL~C1$ zHEVsZagnkgn1`5~d1_yp+RLGNYduRf5sFxgF^$|=!io;cG}ooF+q7Kkf^6;$@i$Q8 zEkdPX*u@xJpPr7%XgdHL(<3%U#_X!rV9v_Ax>#nngSjee4415_peRc4RIRf$;rUh2 zq-<*^&Bzl)=<&ql<*N;8(zdQ459(T(I0hu!egN(uUvVh$$4nTD1GE8!& z>y#sUz)?gwLs*cY`CbRRWMXBj zOui&y%@kPv7HZUIn>FD6BBKm=L7q&WNGFsK2{p4jdB#{4Yp(ltkyG+kMI!S}7F8Qf zGN9IQ+amDBD?D)uD}8bPY(9PZr$-1=IKvZz=s>Lult3Cz@y$9ZBO6A-DWg+&G{ZXb z^$X_?wVirnEL+?fK9P>RjIkK$U1X`(#E>vr@icsC#Kqodr#ccgr0U678UvajQHAUp zZ)*hXy6lbyYaLizD(0zkQNogq5t`ymo)X_|#TF_)_ zJ1935B$8Qyf8&G)cVR2UOl7k0tW9W*_z;BP;V;2uh>aFDj1IQbOp;ARH0@piYmPZ@ z8R38fMu9EjwEYkRzhG3UaCw`pfG`$BVu1FocXq5hd#r8mQvHa!BCEQ~IWLjn6C8y( zEojBkt+Tx{@33ooDP$#2Q<|3@B&iJ~T*=ovg$X)bXa)`f{8g}tXa!wR+rkWmDo`|4 zkdHR2xd8&vJii5Qmbx#Bm$uUBDJxd|5szHRY!vGbB77b05`q-SE1`f2)|6yud@Lvz*Y9mL4t=?;&b-#a zIjxVG&pIfV74lU(MGnbh#To4s**k~TpPlEzEd^ngK1SZE7l-7^R}IWn9hj#I1K2sb zLvmCD@=!x^w0biF49q?C&W$VQ^fB^H@4cb}MlPt+&fL<$d8O_`*Zp%!t-R50?KmKx z6#X}yB8TOYI^pAkjmA!i51&IiG=G%PPLTt1M=dkbHSa+H*GJJuy>mtV>>>`mF+4w% zoOgrI2jqr!-{$cdkQZ9Z{p=JRniJZQ_vzR~zI;$;&S$sWj$BZGzGwFkwB>>J$@T1% zT+0FNndjNv+*z^rBiTVK4({EO1E5Z zE@wx6ies@l^EkV6Q@Ur$nZxPI^5_)l$lnalN$K8NR_FRf2j%D*)aP7#T{K}Rb%1)U9xs?xfA~mZPwdPKGE!57O%Kf?Y|K7RuF}yO5 zo)hW0VY>4bJMcjdpNB}$AJc|kKu_q-2NQ*!YHxW?tDCN47#ndxtD6e|;dFcr%NmoJ z8&h!!ek3esrUIUjn1RhJ$}w5Wj5u*dQ4CSTgzTr|!+gJXM5 zvn{dPS+jy*eD&85n~w1=mo__Jl4mqcpJ@u=B-?p~l%s*t4u>Ty^t9wck*jrGh$h+4g15le2x4*UG@&=jgQdeFT)9X<0cC)Y-*S%b-vRCl~%3^2gY{;{Dcxns%drgD&IiQ$4wui6qIIV&Os zLcpyf0rqy=9TU~KuULSe8-UZ%%6>~yTG@W##u2PUi-}#vh+8lB-Nt9DjB_Y{5fXxQ z3N|4HZQ&b{@C}?Gx3Sl!&}EypVI_>ZojB?(3?vUyb%^U6($1fRmG+Z5yGP}hkxJ;t zuEa#Kj;HLFrREHDN+g7RU=eIgaZ!UcpKfK$OIry^Nfu0)4zly!5>GNhsCKt%p*X>& z_gXvn9Ed%?C^#h7YG?3L4dNJzgo*~JA%dWoM`)7Ot+=U^y4kz<@M$*^u~~WzHu)Aj zG8GI_0yq@NHYV|=XxevhB0-gN-#Q>9O_nU$Mk$*%TZ(#g&1hE1+N%I^D*ff*2$2c< zoUjjh!OB{1Z8&9&X|+LIeBx<W48{4#QQE|QYNG;n)7$T^G^2|+IwPblh&2>3 zNUl_#LHgOP8S&|}PDn@r3;U@plmPf%px8z==x!k2C|u%>^H#jUa^H@1sfu8cE7f6* zBnwt4ZJ#yKQkv=^~Vy+cJGkD2_vZmL5 z6Iw3PJP%ea!hU+so3Dz5{nX~o3-Ei)EiquYdyb61@SS-ks1Sm_DVBNA7adf4_7hOf z&OFi>`x;nr-hh?GivD)NWt$kAV+eShQ zC}})Q(~VB!So~O?laS1gojWL>k^^9q3s!dEgnz2+C+)RnOH31E(xL7|wC!{(2X8F8 zh_;xOt{KKkLd%jziLM&l8DLV!e{xWIw5~~bwy@N&f&r{JDP@WWj`?Uoi}8Z~b3PI;_-<1@XrsqcdT6_XF1+U1 zx%%RMwacfw0wL|s*Jd8U!bMIlYF&PS5vzy9FOHu({B_Nz?_R~H&mo6T5SYD}L?UU) zG;7*IQ6$AvJWRdumG(!+cWDVl+8GpLy=7B!@QyJ}s8?~GmeYh)%PB8bvzuz2&WdFu zhW|Gb6EJx`Jr2ACnFm8D+WW1dDT>xo9HX~1^%Y^*eJqV3IWUk3LA+qA(&T%}G>suO zVx2nsOi!VjtLEKiQ3^ernX;7kZl*N~J2XH&rGB+x>bMX~Wnz{}Qf+|~5Z={Os+=F7 zftZg#C(K8$@&9Y(;0KREXEDw8r)E#O%QEl$w7*q}&rS{a!^t21?Ki*u-M>G1_St+g z{r~+m|M2qgKmV({m}dLa+nC1(Pp{$X?g!B6Qr)3~m$Cc43a?Gq4V}yvhky7;vpHzk z``j1s2{!T7*I_Dxzu)U(fRB@A!}S(dnd|1$zwCAM={0bD%VIa5g01=Oup9E|5`Noi z?RU^;v_G2D)M4gdsl6I3l`n~GAS9U7Fgo_(8@^-fv?jI;JPYclPIyZXO3RLkX+^7o zM((gjvE7frc<*oXAD@r~EzG~4i+}&qy1DewHLxbh z->los(R?(vZ#&QDqaQ9P(`fn6L_$6N|jG(n1K7Wl%xEQzxyo5i(1^n@r^8VtjpZ&#qfAJ>! zi}(KG?fL2c;=R9k?=Rl_i?@D;gf-tJMZt%MGx6s;Va(uzsS(BmX{A+d>Qdpi0TUg1 zF3W?%pjTp6RzTLvGbFWFu(>kaser`uMi4^M6}x;JO8GJ3s3uco*d5D46WgpZE*8=D zN2a&HjIr1N?bSpn8u~t#r-DI{NEbg^@=d0gd;)SGnvX>vs`-Q$jKl@c^WNnS?dJ;E z&r|_E6wI=6BRx#>(IqQ+ddna$rpEk_6uijc=Ms%G(9p4j< zfd;j~ITVg3s2*t%%)6Gsz2|>y&;_D(%eTC`V8uGADi+U2q+qlRKnSZRXfaufBz)Vp zET&9VyJ2wTXlB@8p>J4O(v`+sUE7m$ZIzMmW>eIkMXhkxZ4xu29gbN=lT;>x$;_lh z%GSL+tGl!o?HMkEWOmW;$y}H4?N!T(30>wxoFl#9w#4Q>9QC=EJsMD#JPKHs1Os+^ zi8TD313{N=v}H0h?m$)vhUXIqUaP$(b5RLEjeOQE$;w{H9*)RZSA)* z=Nb75yIdkst08~A6N|Q1ysWoqp%;scrr?t-QLIN+y(Y1DFI~YY0x)#^C;;7Agc}O{U=*hq_HD+)L^mQb8E8nobpUism@(CAHQ{n|`5jHSRa0c6N|%TT>~(D5y4pu0O8azWqXNLX<3?SaHbkx2jD@9=l!=6+5fmQg@00 zS#PQy-Zlbu$*wy=(mp2T05?ms-%>40+uZ&r+|fp4(NjncM}|rv2X811^22^oZHGQI zZJn%c&_~RQIH3Z?ao zDE3QTT=a=V3<=9!vN$PN6r{vj19M8iLxtvK;oP#BJl6*^BH_5$Fy+^M^vpxWZ#gVp z)~wcQs-ts1O^tcLLoBTy4N?Fv^tT*oIj;L6TC^>NkxXf_g3oVUYieJK*P_p=IpzwY zS#?BThB>kOST7$d#re@WGn-FugT}=^*?gK+&Z-+j*h34tWmfN-RR!oy>rBjo7t_{C#iJ>)1L87veMAMV?@+>vFbthY((o3h+RU)%m5 z0lIJ2dWqCCT#u7coBVY?nk_k>0mQwZ^8~A7V^2-%QQmxn{E)pQE|}3{*fMCMTg!fysnrj^tRhm>} zfk)8w>{=DYm4?Tq~8H`h-@BjKppj?$D$pEZeO6 zEzP9K2V28E(TZ%c_Zh$I=PHv;gnAHwDWaQ_0UV5$H_-L(hf77QB2wO#o3v8OrQVZX zl1o-qin#I=yv;1OKtz%ax|c{Yy!3F8jf=2%1#a-)Ax;%3?svU-#PIVtw@4 zaFeE)IpnvN$@91hrN0jpsAlk>DmH0D$hu{Yd59ZmAb}IxMV0Bffb3)m*z#_Ib#w!I>F0iuOV%3viRhV zrRfCp8gW+rX+DNm${;!$?VXZ$5)05-XT0s1k8Y|epN|00D4P5GLzyTA$$933V2LkY zJeL)TcG^f{&Jd7$Lwh8*)8@F1wY3T5nXKYX!7^1P8Hps3n`hd(6vAC-@W4QSU(=+{cABBqBB_8rC}!);|h{`eYzqf5De=YHQU`dH_F(*r9TS`RGr z(Wt1G#R@`6O0CM8Xv-z*={B}&{V8GNR+M7+v9I8#h#%NNf@nP(!t!q(Xe30Df%Uvz zn7nq&>!bCXw?Wqpub+mNH$l(su1_%O`?|!An_c*Fs`b=!s|%K@X856RbnUmfdnwaC z*SiVuGxYj$sL!wW)eQ&qjK@8crv%T4?XMA6`ex<6@mo18~r?UqGc2Ge#wX3F`Vy1U=*-cxtCH+oOqP4*08 zdj_$NXU`zEXAs*ni0v7~_6%a`>w5+7cd+P2zb@!gSdr#fHr|#ZUckeIW-e-UD z?(^&~-usL9{^GsAc<(RX>g)T9H$MMesk=i{U|LdNl9*+c;Wbg&FBp{!>S@IZ&GU3S zC8zS(LH>=?i*v*>i9;09U}zv%C`xHrVhUZ7!NE~5W|P>Fl-;rvF{6_pk!QSMC*-w^ zwPI^B0JkOuiFhH|3;>QQxog8SKr9P%KC0!m&qoMDNJ^5^=sjHp1~}PtH3dH2mJ-qP z(UhE~=^zLP;3yOw9u(}yiN+M>iH;hpp$c07;lrthDoAd9I91bHh7^V?NaVauQ!j>6 z!=zb(UK*A~d{fX>U@I%G2b5__-rAsc@bQHy9h?WrItlEMazY^+*N${UHz=GiSPRxAr-NxT~u7oTbNAXRWBgx5zm;UgXB`JzCe8d zS4i5Rl&_Bpo{3n1AauDFl1W@$A3+Y>qTYT7H>vV{lIP<7eV&ySqjA`=pld2%vI8uF z8+yygg0YO`1&>%+qBaP|3)+nW-d56=hiW@O zjk>3s+S5(#>8AE{Q~LSuL^svr>!HmKYHsj*gTusO!Qp`7fuZFB`xXeS9oe4Yag_w@ zF7xLRY8L?`D2z+Jq~SCFoU^so|iA!kF6 zqI2P;je>oW4_HbrP&fJA&ktB2u7?s29FtD&$;9l|bzVs+pW|KMk zjF7GZW!*)`yw?U@lI(%Xj0F}JYZMC$5ZYc!th+pzZ!KHJ!^pi0f`tA7QKWCHDM_bcnCz+XXdV}<7h7t@g-hmr!ygc)zj8D(j#*cAQ*XSG<7e4TIn+%L40 zJ)a$vn>7R{(p3Jb)X>F0>v~}&_^7=$p;(L=&nEw51(#kW*d7H_UbT|18DV*uq(WKx z(`!Y4*#{a`_Sh+OAqb9H&Je`OGv)A&lG5*~qS+4g1(kIS&rwb>(Eey{~Zebrllh~&?9ugtR`p;4=GOY zj+${4)bZG=>3(FN&_+CHHgZcLLuo<%m zFb(Nqm|Ry6A>xE2i13F=zs7TCCb7 zW3kw>Djp3?1!;amNhwrax3-rl#L_h}L1;1oMuDV6q;d@guHWb~mFig?m^ML9zx}qB z0-&?}Y*cxtNV=PWFtxGnyWn99Dc>n|7eU_%pKm~yJ4FIMHsk2y+)#1tV60T&^qnpU zwS@_!uH{7Skuxi+O4h7|!=mAl?LaGcFD~0VHP0a5LroKm>W7>~6^<#OxEdLaG31c1FoF~Zb^A`U}I63 z^oHqmj0Vsa<7!dLXyGVopze~WQqp9dU`yb}Ar>>R(907B>KOV)low)_^`&%L=>m}mC>L!hE0&i&FbPbA zT#hLdgJNkvg`cqy2@hRj(*) zfM6A;y>}#*^haiYyofC6hl=n=W=V6ZRlHTkRl6gn0dq3aXU#~=kBXO$dQE`G!%X^O z;vX;uMf%M~SSrK=#-m98rQB;MgZmx^Z<*j$^?XMxOwU)mtO9`unW8Prs?Vv}nx@s7 z59D*!b@7^`HDuA6hZ5WSqdeh9N)UnhV^Ej(AT3v9bmdJN@&K7$qVfipcYw$s zit-lf@i5T={Cz!1$AekV!uif5F-?F>;$=Du<}d4;@iROFu1ov(?K;6l-N{o{V;&2Z z_%RvE9-f;lh?Q)JU0wl{!`S08En8D^&HYZNb+DJt8nhGURd*9d|!Q-zV5^E6*wrv zdA&lZw0n_g2WEK$(`Or6>;S1A;_OyJ>_FqQoih7lP+|8V!M3@f10=iYuRCe2L)yGU zd>uw;9oFavw3T})wMSADL8R8b=&S=bVGxnEMNl0eGVprVk7d1^ytl)%Ar6irMg_ws+RNM=Wy1iL4T^TaIoDCKmw5pnfskAj}X&%7@qJYfaWRR0q%M?IJz%5M^#HCBPRfsiQ z@I1GZPb9nvul*6vs)DB}AQ}6V^+ycMrQ|xjWXp0svQTRh6NIc1!8Oqhs=dKN0euE$ z4oS<)V1R;duoBsi4ccTeE7GknTI)a{Id5S-FJ##;z>!W&0$rU-HY3keSngSXT$#uR zyG~_Dq$$6{8%8tlFT^oWbxv^W^o6(@x~8~u=0e)3Bb1^=%eTC`V8uGADj@t;Io{0$ z5Y+#q?6m-b?%aeWh@u6>u5;DM8&;Ndr7v06_TXLzNH+W}hT<8(S``<{@ivJWrjuhL zWPn7-i&q&^i79`|S`fa#`q3Er#yiD1+dPI#c69>g&ECt(CJ>Io%^!6!Uc5*QQ+j(&tD zdl;Yd0|pgTiuHi7g8J$G0}HZTU)fAdP;p`j9>doqJnKs|_)uQ|@cJz7$}CDwzv3+N zeAnSk@bG=hF(LO_#jLdzAgg8t2nK@+yfEW>^Q5e6(SH2`FLx->{Gm9DNr{vx15WD4 zOvd_%RbF}=-eHwp95r2XTF^CvM0Mr4h4iNo{DWr1m#YhS2$F22rV`^dy5K_~XFM+Y zOgAUuSy>gEsA^xqLsqO>YS4JO-UuxxNv{+yqH>ml%GvaD{SGQ&6JETgSBd0+vNg4$ zCZU$ey$jY1DW%XG5;3eiO?Y9N9=D^gk~D}$3B4Bvmk}G(L8pb6gy~TX#uRu793W>In6}a`;fuOi0H(93OSq@2y9Vq|IhWw5 zrlSh%Jqo!zMhTa&;w^})2p>~-*_L~iVhJz8GOYZ{;KD1twrU?ALr|HO9tg`Y1GsUq zkUl!?6*KqM+HEvoq=N6O!5FBFW1H&kbd)6>6Y#mju?I+04m49IeaX`l3(*&wl$F3- z_>7z{^(z9Tf=`4=IMt+fX=!O@piOee(sY9Q27cYfwV>r=3qthxI~h}?X}Trz(M?t5 z^AUi5#7lmEC?W8WBmo};x}wR87tf~#l2~h(VbBGt#E`gYqxWNNC$Wqb_(HkfB+xLV z>lh!F%X?a%W@{471P0wUI;L0?ynSo8Iy5`5JG4#Xxaq?V_bfe8oAXIybDC`cEQKM8 z7&gi&Q4*Cv46TWyQ5=fk*E_hoVbx7gI~6W5W)&;ekoa_)P-vnV$AVWp;%QjnP4Uxr zIJe;KeoB6#S^N{UPOkZBts?<4JG=T$_qC-b0fJq9?dnN@Slw6Hq{9j{_w)bv^!@kn z`}gtrNCko8RM$z!PVh;&r zwPAb421o^w$DWa4Wwj#bH&A^;!yu9cT#$XM(t68AHt!ArGP{aJcHJ8SMDE-yJa>kW z6QMz$@4YVs>e|2(j~#b~uybdC`U<;VOdvZn@N53H(BtZWgLTH^Aay0dozD;bnz*6c z^lOxUgA*FxP9pL5Ad}D1g*CgrOUiWpivjVq|@2+m|tN@XH z_Fmv|(J#Iiy6>JJ`j7re(1H&)5Q5|AxWMm2de|mmC#vr0njLfBVLg<@&jV-=A3AYv zPkrcp_SA=ao{fNK&$H3~?0Gi!JR5tS4cD{h+3>b*&$D4k{?+qr?5Pj;)Q5ZO!#(xk zp89Z4eYn4p;j_PzwLbeR+5Sqlzmn~*WF24JU&*|$cu?v?KeV|8(wxLBt4wN4z4|b? z?DIU``d0Xm;!O!0K6I0U`opp6BUvyppbgOm`$RB^^{9l5c!N3?1ab~ zsPY}2(p?Z0OrVaU%%PNpTY;a9)=KIxwq!o)QS1tfKtjAVr_p=5Vq?Olt0^#~wUp`d zCup4R)@;TIsUl>t%Rxf~WD*#i+8s9kz<5Nr4x4^q*Wh@B&g+z%^U?Ia6|3>tty>K% zs)I2`jg~LKrAwx(u@K{GSLY_0VeOV>F)y6;X$dmdRk~XFSl28rZzQ^qXSb|a0efMR z=~NwGND)hEYxAL-90>&V;V}!Q{SV)x&+21H6k{s z+2D~wc7v+7ipl%rT4=;;i_T9{B+hFUYQ2#@DArmlcs{KmQCk;p&<;~#8@LGE$WTgI zjJ@QM^ddWing*@iQDWCBl#7=VYA8y4yMYs09|JsDkXTg=Z6Vnf)rBM={oSsLTfR^7 z94eM)WyNSbHq5SwC50)xp}NS-#F zj&GxRvTxcjIv$Zu1Wy+}$Eyz<=$#I1PQaeJKPJw-bNv9D328mey}xP0tc1M?&%Utl zUWVjZOvv|_-(F&htLKZh(hIOpz1NLRk;)hMu~JQQAApowp?r4IekHpe6j}e*0e&8e zGXt--7SK8fiD+k23yI3TfzoydDlA4Z0AadkvS@!COcoULP-X7=p2%VM*%LYJi5&Js z4tpYpJ(0tn$YD?9uqSfZ6FK~kCvph-qQ7~bW(B+@AZXfc~_lAM* zHz&U1s}fr?>uidxZr?~dK{+thc9XnwklL!S87|AD*rbfm6-~0TGF*E$T(F#yS2Vhj z=O2(&$1E538dvq5WXh(JZdbfP`M+gkmn2K_>>16r&z=#UX;g}wjBcSNDQT6IP&7d( zzglUKioRzJssLq8)jZwZ(>2e?_4T!06KZ-5>PcFbGKa|qN!_xdOnBySVrwC{$MaE( z=-Xo{T{J=V&3|jExSSX@hkDFy0~;e;Fmzk;>ip{KSC^d<D#a1La_oa1t(hXd`U8{mm(BMt8>LjC4+EU zIR;K+a;BfV_dhQZUL;lWPrdEQf(q5e8+;$#%Szlri-eA0 zfhE~Sv`zdvVd5Hu5jkPG18BNW@}@2g+)5638j9R5%&p z9+)}MmB2+ZAHDiOqiQ~qMlm9}#NY97sC|IjE2YKCwf`kYIA4=<(ThxN|YN^PD<-<$v-w?Jps+D`J!VB2s`@(K<3tH0@8f7Non!wLtd~FBAurH zxL3N*g7)B=t~&_ezDgWBkQmvF(00mI)2{c9)nbLov$$W9jPSxkN!*~XaV%R&?j#0^ zr?G3^F<_&2OjIx#vrN{ktmrzQN;&vNn&*t7qd6%YV#UoW&2Q*}RY^os**Cx+{H&`n z{4#_8|7AY1N4ry|Jquz@i+5y3A9N3Or$n&@3z}6b%*V*<^UHTvMiAH>2T3NA6GSJT z!n?aSzT9+c#`I-|F8D3e^96nVD;95(Dn-4|Ayu8gz$*no(Q*?-tSpzCG~E(fRTK;& z*^it;J%R2-Yl}fe+G;|(j#Uu##o>z@Rf1Zv1*O&ZSNqnV7c5R9 zT7?8iaKuf3nG-5itHQv)sUOE0^yCm)KbXhL5M^4ainS=LE{Xxqs@O2Xv($xTOO|ks zSk?fX;to?(z0VTFG7Wo-Bvsw?vTUv+#qo3E z&NI;FFleR+w1$+9~Xhs8%!OTxpT7&|U4hkI=fYG*+qs!N4 zCwj}>-Q7)#Wi(-NQt<+|U9pVRuA7Q2Xm7n~SOKGgtq?S#pmr)kE5{$FMEcHQ{AftQ zWI}Fe7N-)I0@X{a$r&GgL1c+hN_UT*i$_?V_ZgDZdUBL%`~#wwA= zXU)|Yxab?zdSro zlQ7|RM`^{4BLfXe4Xc+;4%+IW(X>hMPMGXY{X_5K;9R$Ud2V5VK+-eEt$%)7Pi@*` z=t858L8)6|xop94$>1Ee(EPQbxoZKPAC|MW2Cr@(n6LB$H}1D<%R4L%gMh;MwU~vl zqTp`UjO!_<~ECs8^Qt|f;9ijD|d49gna$#v^$oCU%B|slOjDK7vKB7 z@ztwuUtPH?wNJ>|cW>XmI`fJ8?&9kFySMK=-@3XyJ@dWq;`Hk5>wwMURTh5ettYl# ze_v1po%o~Z>eY6^**XBW;fAvU5?=Ju`O+#{DyuEjp>8VF*8_|x<6)!ONSBt&3x1o# zzQZHj?-6FxYKt~_E*{6I)mTJ0jyo;cvDMBH%X6Sj`)~xaVdl}K&+reiq5R6xU(jo5Yx zpP(6GLf7>nCz0;i-fryU=@--I5AMkBI~u=!Z?=v2=*JRdGX$x8XIom`UmPT|=~J%f zVh96iQ{=p4>6WBy8P@krCK(3Cqy+avM}bbFoGy|yL0r<; z{Epp1U>bbf&k7dt^%|7fvv7oX6J{=fX-y~~^T0jqr{*P;Q-l97{Z}rcK(|{p1{b~9 z`}XXx4>^lucmQ2wDW_o$_XS|lBnYiyAF7h@O(oWTquLfg{6lyB_oX;=tP)nJv#WwG zA>N=+Hfo&)$+gxv9B8WzEx>oTE?IgjPF*=oIj>k&g?i@I+KX7(j@mEx4*FlUgT7%H z^vyXnS*OaQA9Aw^iy`mcejMKVAdwAQOL_J0)l8RHzr|x8B(vMA-{r9n6YKHn_pU!h z_Fnym^y*uCF?8?;yY>SU`u48<|B0^sUdc|s@A!th@6J~^$G1yvhs3tpPqht)CBAB~ z(P&eEsV#o1x8LP1M6LT4jL6xR*=w>pz}M>3vIcXh*z zv@_Mg>WsCLn$;RW(36tTTbiWkzvw60D}nfRl3_WLA@0Y&fh$(SBmT2QR>ax;t!L&WSA**a!kxBnxuHRiiJi|K{`_UNW0alZGuaA98bw) zGQsFaSrr@SEeD26ap>}b$g=%F_yP4Qzyc>!Id)kZ`#5K&Qh6GkJ0>Em!oC~FB50L9VJe!ekfbgWH#n3)lKw{A;o<=E~ zkH(NT%U1=$p529V5b*vLTNMyXSuXDBZ&~hM@EZN+cU3_vw%UHV7N5IY z{+T$duC_V*a!qsnhc`6WVy(mbZ$GYCMQ@L$Arzh0e=m7AOExP9E_IWXdXq6vUNUX<1@}7JLo5LRvLo8FJ(T z!0jHAMX<0gIGZeHMY@%-4W*)PXtrYURAP(7ao~%+2)zlEK+@vS%+1(i{XWemuU_Jj zD{MSC3B~qv`?j!}{s|G6y@_~hfXl|~qFS@Eq^mv)K>h;n`Zt?3&A`(N+5P%6G>H7( zF?B}J1($qZW<6-`wyg?p!zBp2p*i-t!?@9x1fr1Q;ESoeeKl@b9-%FU1k)oCw%3xZdg=BbuaivN z^H0cOz#{WkCoh1AtH;X19~i5PibG&k(Dj;DNn|Q!_-XOgmX$DL5UU&`dRCs4QqP-h z>JfZV@OTpmbDA%Sog^Zs*)Y*b)+H6l_Q+Kzt1MQyDdopeSW$Iv(s!~ziS^Vy3NdDy zRgA?*ac9aph8{900UZ~t+`Q3^;JC(4WckMf6Jq{peDdQn z>z_Y9{gXSu9H>&;eA>;Yw%F@l8NG(a>LPnpY}gpN?R=U}GW@xRIt2PY5-)WJ|0ZMv zZSs99gmo}*XnU5|QM8hl_RXv{3nz%UeN5#QXBzSKOn>fE0phmyhGtu0jtRVfG~5s)80<%R%NlHE1T8$d{WGOQTLR9<$w(?&(+( znz-F~_&@|X;@L7yB9({Bi;89yDwKk)*awnSnx@BVk{-lad31FA{M}{|^EFMf*XwHb z^iK!>*w7Rl_;2Z&yG_P@q4{>z;kze zRn~9tB7-|g~Nh$iwT}t+gp(= zn-3+#RNRK#VG3ApyCGNfCU=D_yOv>ycW_nkP3|%rHNaD<4xvWmcALKcz1Wn_AKQBP zFXCOwdk8Pq@m7QsD162yGwFU#V_-h&Y}I^(p@1bT05pHaRrt>-5sJc{UYyJJ9Y8HN zTFQig`RJX{I1!tVFtn)}{?y%0?77O^U?1`{iIOVani_8N5jn8^T~EEG%WK(suA4@1 z!5XtYfBT=0S%4v=g6ef%*lJea_;9zp_U3KGe%cy`? z=}d^UCiJCiJ_DTrb*+3l`42g#r<4EC(<=Yp?FR^&S6^0DST@zL344%T5;&^Cg8?z! zej|cmU~5?L56!W0j>nFom8iISp%Wg_wDtz~-Ao0or_G8l3L~I51Hul3R@JAZ`~HjU z%t$|L8nob>ET#qI1f7Y`CgwAPQ*#Oy%>t+xSg0~v2W=-yQPVW#cd}F}U$e?dSV~Np z!CKT_;m5?81UC*owd2Sky39~LoMMY4;Kp)*UaEforJz}*(%e0RfCSV@L;%vm3CkeB zW03kvT}Jg$c1qPVu$x}~FVk8d`&>$E~HdDMgGFZKztn-w~(l>etV zob9A%cSCW&Xr+L2xfw#WU|}c^06wddp*^lP_{GyHIj^1|fX2!$#LVT2T#Na;CYvk; zRE@`~G}ft3;j^jK0pEjDjsW63Pq#i6Bx;l?befig=Q&5dUgL_auC3iQxWbn0v zRuHy%Wb{=>%wgIWyt*NDzUZDLsvyNs^$fgLIxuD^x5ZjZdA7p5RQJ+vstZIv zlq3N>d6m#K-A?Ohs*qFAsE!B#2|_nVm9!G%nHcHbNPEVeeze%iJt#EC*qq#B!V6ve zbV|;boRtMBQni(@m z*{%mcflA#M{ucHM@yGv9`T3tO^rNFCUV;Az{L5?bhbw*inby$O?cL)x5-_4Cl}S=`auA%wkCbS2>5W*OVIZ95g)wv(#Zwr$(C zZQC|0wmqr;_jOPAtm&C^lgq4iuFiV*@7en}PYZBYW9^>DF`Vbhp?z^$s3-3ip8Zb| ztgq&l=VKMGI!3XQXZPbN6tA6A%7p~6)IaS5mU8NN8W29YH*!CKao{c`zgNsf2D&#(0avNaFY!GzM#35}s; zH%V&MX)a)AJ{1KxPRAohsTH$XZa8>&c)e~{z;9k2p33j%lbf5N2=pABu9y9rqhtCU zpWC}5@{o}kJv{GQNKVg(ZJ)a_@{IcYA@Y!CZ?y)8o_Mi~4r7U8zTB_~xT6Y+l;KhO z9#5_`>0d=|cLC)k!uNq;+_;K8yXC1{cwXULyOv6-o>fG~dUYo82Hj!(H348_OmwiO zi});ZWU-Jw6Z>YcQikP{mGNi48vsHJKt-t%l60$()5#k=6N(!O_;3#9*4m=~M983( zSQ?HCd(STIx_#d2H+-mQ?$F;9>+bNJ#P);0j8PLC9PpCQRMD zA0)~z9m<-w{A%yyYyf2e9dMnop3|f9__YLBs1$Lc*>u?Qp5sH}-i(7~?MhRzBDN26ONOM7Wccev3J_%te z>^~llXrt_gi--M>kmGA}AL|#4Mc0#5q&zC!z_l5*KhAwdtvf$P4DtGOe7%07bTGEq zS4mU_MVr!W0ukB(`)O;8uIHPJ z{~w$k$nKcw*?CA>BRz9$`w3{=PRC%i5%6U*zW#3Vpx1*iaJ;irw?}tN9n=osHn$wR zty-sA|C}9w{o?x|OG{BEcGC&cWEMqZ)$~1^G|Lhy>n7MGhtkMp1Orkq-s}PGJ{OR_ zH-o}3ma8bM;5PRY9iF0!elC+M9ho9cRY(m?e(xQUf7iEiNqomo;%R}`A_v*eH`y#P zQsO!fcjjOw?C4T;avehmg}WG&X|210#*fk%(TtKSD^rs&=&&bf{ph1!xa2fEhF>gn zPqHIPGvfZHDTKG(HV5w%h?rx)hRD;bI~C>)1GLH`t_KrWiZEni+|OasuX+}KW=WW( zQdFb-@rBW6LdD_#qZKiER1CyZeZ;o}&css1t5^cy$76x4c@P6gc(Rei$Q76xdZ74D zV@CFqMo)LH@nsb|u@&X$rIgo?;F#*b0hW-Ar*RJan>BhpQ$AoStmF^T4g8-7{!Hwr>)ZIf%H&k57tpw-BE2t}XyaJ8y@ zrT^ysMOp)VRjs!)5h~ddczr=23onh{9nm(Z>q6Jp=C4n`!A!jiU|}or|9kH5yJL?p z-rwV>q+0nxjMoN$fPM~lp?Vvjn%7Ixf&^8nFC#&Qy&fKF_qn}%0jC%4%gn29Yu;;A z3<@I&39#x@GD>&~z)_Gzz~1YXyWeNtE80i57B`&l5pig)(jkL* zH*yg3s_8}I%Z>a;9)qXgk6|EiSL=77#gB{FVkMmn@x`UAinBZxv z#my6}_uK;EO|~y@_6_c((+K%h|55hDvt<)bybGUghZn@k_U+e?{%(ww!b`5z)5XDp z;Vr}$7cK}#5-`R$>{d%W2|F^K$+@nxNO|#LS^S)VF>Lm)cC727XsxjH@a45 zV+83g(C}?aW`O)Jd2+>sk1kaIt0sBwywi7w)9)^aQzKUsJY3DIpOwGC4zBH8NQeXS z!-(q*bjof+9~2B#tE-c9-nq&EB|jrXC-;zn9T{jh!u5Q6>YoYYrntzy6GT1I7McK6 z__Du5a~hYaQOT-NUzXnTz%k+isc(}|#TBC5%2^=$;_9f%*`rjeS{`Ok)=(x45!ND>BYvY(Y%iXA6xh)nJ68Q*25VZr=xX+L*#<5OR%8M(`JB z{iy2m&8q_5RE9ZieYYzTPwG|qlxir5k-Gf?u4`_# zEB=P8S-CerHb}l|9!|%_PRXVXmOI014Pq6iOHFc=`^{Bv*!(Q9rMt}dCmP+x6PP0F zec5zcnbN6KtPMr})P#ftT#)la zgI11_Xq2E9h6Ot2Tim}1MRkuxJy*igSAZ-&cqr zPED%5$A++6lL80fatp);Dg|oR=j*~_W{+s;+jWR=i}WgNz+hg+M}V4o@YdtFNMMwu zJ+l&MQF2wF*0?z1t*n*3y`So!0Zs`q8nt=WofttOiu-*+%LO^Q^SxiKASU-7sw&tv z`V~p)%;XkXp3e)*aI&^;xQVAy&t6YgVt1`7kdK=>`b+ zdN)6J?EcHAnL~gE;{*e)z0#2 zKPiLDcw6Ta5}wPz@0AFim0T4l+&thL8n$XGbAWt6`=U_ktUWt>p<1ObA)uMJmA3_H zV2E z1p3(t_yUhi;-O5|vNw$RVh^7EM3Z=(PmsbO|6RY=uL5lyf=0y8>tR@tCyAt}W+QQW zKoVI{jA+z-ugynZ2cZ!l9`_m zkC0I$HP|z>-i`3c3n!D0x8l@MRifR7GppBEVd7oK=4t{Kgs@l+czsE=t15dS0+jNC ze^ORr*g&JxkFt{#ON<$G(ZwabNHK%$O1?jOwV=R8| zL{tqSrB}`X(H~uBILL(2{zEk*qrse#x3L&grw;1{1~AqR=21B)T#+&RAFgPxuOjtK zLopGNEUwS5%hRaT{z|Rho)h=1-~hE}oDv~|KK_$Tqg#qY#28?KSD;rVVn|DuOTH?( zK77G?8j7Tpv+Yb0)-2u<5d)knG@e)2?1=q>C@GFUK!hykaI#AyzrRuPsJ7d<^ssSqr?Y~A!8 zhEdmEh=;POUgB@IV8@g~Xf?Ey7NKcw&A7Ss5ebLayNX&n+pm90>P|qh_F81*N=?i< zMbP}ctT(l4h~{{(I`<@R4*V?1vyRu%@-a#)4)_-1A*Us-j+d&YA0RQ#4q;N^e~`sQ zN*O)P-P_-;Ri??6C-Tojtpc1de7+DvDXz|dj9N){dOMXeK9Zy%qwL21f|N;A)aai< ze3eeeiR^3}phB|k+d*G6=Yff+u;8AN2zfp#B zn*Lt^0zzhWXh6ki2((s-D6rM`$387{CcECgYR%%;ZUeLm{0v{_dK^@U&apSC{H|71 zr&8x;_umkLIJ8Q%q8XB|3EPG#E!P8Bs)3V4ve2y9I>Js7U_f!KmtuTYncUv_CObtb zB%hpCWVNI)dLq*(%jz!uUK6q{2UqbAv%N-*LX62)l$X$0{5`pem#_9!%8yPuAxtR$ zKkT6vxei^0b?4Po`fovme+`ssvP5%wOeR8<^hsvTYI!c>$xmR%GkJlk-EANo^*z|C zCvfG2ucrb^vu^20N`Kn6<%8KyRmaNV=_vj08@!Ea^^j)B^NzQ6L-;nH%;OGDf6ce< z-G%WlrUJ&l=O7H~8?T{@n(j>a0$NKuDkHLgiRZvVo`8&s_9Le5jDKlbQmaPC41-RL z7#?_=ZS&&mL_9KONTkrx2&8;pr@ZHjIZItX-OOEosk_zG$#=a7nW#ZOzlJu_92tS- zvRq-+!f@Jm?mELiAe=%+2Q4#?giWJ2++22tqSZhsL#LpP6f490p8dfMdByT!qbV=@ z8c?P*&3dWl+S*TKNfr7`kTJ8PQl`inmeUE;=lYngUcBC-(|15IJ!4TW4%2)^)aD3KK4biV2CYcvSsNjyw)q`M zyPD-EthRy^&7r_mnd;WmAGx&YU%{J$1ezbhpa8<-G*8RPHC%o@dy)13@JoxV%3WZ^p@wIvsT5AA|SmA7XO|Y zcSQ(T?#;R`ElS^cKfmLayas5nGYWdZ}#uJ^{=KO-RJQWRu)zCwF<+5;X_s#9W(P6 z&zwHHD?IaLtL3Y|GNa<1i@1wvv51Xh&$MgBAsCu3nlHUn(;!msnu{f=&V)Z|X_cv8 z`{!wSqK}bH|C?Gm#0?7tgN=ttMx9V*n>oLR!IgzpU3f2C?SFAg%lLI}7D7a2{^t4J zLB6HLQ|D>T<4GlJh<0n4Mh4+3Nr371Kk34#rMzvQE6M*aUQp`}4;WTHgK63`avbdA z?VJ5S00yGu4s`61l%l-z7cjisX!>1yd(XZw#!FPe%X*({Yys4nkT8*Tdh3$})+zGq zwPvLnZjWYaxQyE3OH1oVEQUT=pL2HCxISE#2{WexjEbTh1f7m(o(3@ACQQ^$Y6c*H zX)4F|fjRtJAY!rTiEcR0${SuoT% zZZSipY*Q??dqRg`vd#p4fZ5gJF_bMB#&fpeGC^VTm57|rg)ZO-Eo(Ocoq|W zH^SD4qz^RHo@bej-$kJ@hftZr10%H=fK-`Mc}`yOR0X}&31g~ESjbi) zk6_#IZr{ycG}zxfPSIjoh6(`ZW$)I-m7;H=W z%gQc7T9_)psY+WVwE-$k3A{^`a}$;8)46AB^?euRPG~AI9<}fT<`hDBnB(!si5!7u zOYG$_z0H!rOx5i%fci$bz3peZ$_6H@=aUs)j$uPq$3}Wi;g@Q@I*eOwDM~d!7r~cw zwOIzBtJgYfom~LjoGun0dPaaxdy!RzV*4t#n&$F~<`^^tzC%fyA!AG2RzhpqTWEHi zp>s==$g8^V4ejkQzThTcGl&a=V( zz?vC|x!iMiTvkVNzJCIJ&l(%cZf(yq4EF5UQA15qxYi0%)n*0Fqu=Q1Y#dzqwE zWf<4iyc5<#^uW(2gEr1{Y197C&)V7o zHTEr>HUVaV`-15(_Eo0pLo1yeS)8ZG}+z!#8z?84;NyTjS`I)Ex_ zImP&xaj1j+eDP?6rp;FS#5KEGg_E$_+O2uk@kmJz??A$MTXamxInmVGiLCSamGQ^X zOXIQFm_HimgdW0uIY7_rt(XS2I9R`cLQIr*G_tRZgxJEAZ{%w$Qv3qt#L$nVyBlW( z`U}jP;`@adQ}5;*XJG5#I7jRanGyMrT(M3?7FiUm2P?Mk88}fgbai2r4PI=@G-<&< zs@4AecI1}zAebDxA{F5S4vRu@{VbSHO_ERWBI+dS8tzo;%~cxyX7~%bsuFp<)=i@< zFaVK}Y*GH0y=F&``UcEe@3ZWYvFEkvq05{Rk6eRM@3?V3$6+Za@k3vcIL=#iG>Zq~ z*MZDjVS^g#rZC;OXBUmK(zjyD5bBX=Om*lgmZY8g9)EU4AU_W(Di!e8>s8|43ecg2 z4p}tIveNenRO;y(y6=w<7$1;>x zxsI|8`PQwbxLK-R!hToY?z3MLz5fC&uOo4<@4HCp6PZFGConj0#)cM>gv zybEu{5n)QVLy#<&-}d~HTHmD^{hI)-4yD(?Lmx#Y@v`8LX4aL9 zUulAp4}v5Gq(A5FBp@p{ll{}048s;zDN$(3LbT44cG#dz^g3Lj>O_)(vgaa8QEL*1 zvr_&oq>5owYlzzU8j(QXUCVlJDLw53Ej(+hQm65xu+OewpL1l^ufnq`c6jo%_JUS+ zN{hs;mu&Z=>}A@Dtt`{{rS!`PK-`~xJYh&wEq8qdXgtGu7IFfVa+#qT`KVmUG{{-? zh0@}{%-0=sMVE1_ybvl}|Tjtx)zi#LMuEQ-DFJOw zy@_ETusFy>ug<2wR!bNSoBK>2ckK5%0PzJtGze>B<>+O@a>{V?ua=(q%WRRKWa`Ei zanV%E^?41J86Z;ebNyM^UACrhi(uXcFt}6`H^j4e{y++A=M331u6q_EL9f$n?4~ue zbytmkRH9B|bC)kYves-*MY~Y(N{o@;DSmmGFQAN6DjkbSRRPqO%KP-xUlGXD-YSLZ z)?X@yj@aA-mNsVV+sDH-xDG9>gKTZdVE{=Km0$x7k;S*m(yJ`eADg`^P~GJ^h?mF#M)$L$GK+{^7 zp7I+WRaz&dTj9slpabNT$U#_SQ1MS`q zTf!ro!MJ?tp`+@+7gHn7Z;X9id+A=#BI7Zg41{6R{mb;1kY*brI3*^?EXzj`v1dk% znNF1FWx?cL&Db$VqvgN`E=ARc1P9uMsISJi5Q9@(98(Jr%9E<4l0IGB?><&if5z6J zdip|wW6ZH%Fn@%VZ#7Mlnfq6yLG6rzx?>Hy0!bE(1*p?m0FdP4$P38#RmVk^VTXj7 zE96y2ba0S>Z5D45An9kM_DU%yK%SpB!LuOO>2*Sp1N&%A zhf!7)SGDNu#ty3_b7V=NRF-u%;y)flQ(eNU!!6j=!HJB@{jec| z&FZ1U-1;7rzS$2z0oIA#5QG;|n#L4#5Bng>m|7X*P|Te_d1hAuHX}$|Q@5a#M;nFA zVX`z@FOx7!$%Y^~aCQp2gvvXU37#%b9+gboI+xq*U@JZoZjjWXma^o^YgsPr$sQzH zRxmJBj=Qi7tO!GUm&hXlBqC7@?SG_(XQ;T$W4l)Z_1tQUkN~k8SfigV#Oq6<;5=D) zbH=qn#makbY3l4s15DSm7^tzau6ATh{VW%otV=nnGsF*#z+yW8gp1LpC^$NY)Bb`3 z#}(+j#5S=lgy8ARDwGk8_FACN;-0U8-d4 z*yrd&c@l8C$R`r#XfvNt!@*U{;69RXiot(;a;1oXEB}C_h=2jhS7(&xaJ~5gm5E=_ z`r1d$rGMzpbxCWy-4=?OnBH)I3pyzpSFgo=CEpqOgOIaMFM1O@SS=z1oG5rrl1B>r5g1&zroK2hpZt`r}R3^u3^9 zda}&#-Z!%GtJA73xM}TaUF8L7&Ob=)H!L|+?XxphsOxB}GCc4?`E#k6N{U0U-L~#S zcq=P1m@mId$aY>8gj3VTQR?%X*ssnM9O=;6tTT?hnIj=;6 zqYH4fttpyk>v)^+52iR%c&^JK+KL0(%HP`9?AiFt+3@#WPY-o(Lw)K%dwH2Qb)dW& z64v~DeSS6&6hpGUDAk)#j4^ZiC|!Sr#T9CrY*$vUd;Pbs+IBbM1co}3{dGzveFe9@ zvcz@5X)N-rYl6;bc6l?UzTc;d9)LEpv5R{+mgDdnzw_1nzf!Y5f-G*<2;{P0 z=(an(>F-LZNRo3gK(@2@YdI1o9S^P*+tm~I_0hsL2nMveppTD~nd?I^p~-?e(p>04 z2>DU&8jN^j#FoOaz~b=WKRiVij`l>q0Dd@1p3t<5in+s;nHQ81sUbMV3zejbt%3)E zngH%Cr+Qs@hTBIx?Riwa;TL!H%`3SE9Hls|M;~z_8xdx+;Ukv~LbFtD9}|;_JmgR~ z7;D7u{2!{9EG^aza?S8YAFiN3+U9=%ZhlFKGAuTE7K;y0A++2>Vxkb504q+J;RK2Y zFX`=NKOZ8OlSql3U@?b9n3}Q+J;co64MOxx;0U>(g+K?HK!qtn>ojfDwu@AVqd5OWcfS0&}xZr1* z2hczq~4f@XqYSjZ37kN8Tr9t{sCm>jk)LywNab|a6AC)HiH0MvY>Ca zeNHSsTgwT!Ba{Z}F*gupd)PY@yr5}B*mtELiX*t$ch%=Sn|UtU+Wr6`(qOtO0>f@} ztP9y^OOpea%AIOe3ZJ==>YN8PaI8C&|LUlf=>JglEFI%em--5ktFLRQbpFd!e16;* zz+|x;8_5$#-4c4`7^?@jpC3f#$m5?KG(+w1XyX|HrF34C{NZQ~m-wiCPc0EIEkx(e1SOeh;#6SqA2DE4a-DpL!g?vViM7wHtqgnBd7+Z zkMlg;pQ#c3L;k0vph3^)^%b(TqKGEADmiz6#}e*z9+Hl1!D;b#Idmp?_;#W;EDe@NOzjRQKnYLd(1EEd79(mUVTmxXO?je@hK_z$fh(;gBYD!v#=N z!HS{d=JPk@=9M#tRcBtNtz|egO;CWT7O=3 zodNJaNE4<4(;{X+c3o<`zA~MtvX*rkqatL$70F_t#;{j|{>2Uiz<~h7k%_Ioxjk@U z`Axl1()$hW&rO}V^D-{E94#p(nR-hG&8!)uc}LQB;Q-N1qjv6{ucsN2jgP-{P8!H^ zukEQ^us>yP$i;eJY3*($*o_fUkMY@Ct{8ZXG7tQ>gdY$_Ye{1|si@0!!V}>8h*$>8 z;bN9F9tk0{9(4f8C{88Nsr2(>RjY|>-nwc;crv@Pn2cJL^uq$rU6K;TVri&!98pFp zIG=R8lg!8qx#w0heLL;3<_+Xf+AxSH6R|MV9hK2@hr@oC7 z8yfgB9?fxDbIl1uQ?iXq6!i#_xUW^hVG$*o0h%?zWM_4jD2dJ|kja4sX=hx{4>jj+ zcuc7`^f(o@2FcJLTaDrqwFYs^A<#t-Y?V{>Y;qh0&$D}OG1N+t_R4MUp7y~=RKk$r z@%gdYv(@PX$A)8jGss1sNb7Fq*b6an?o{=1w|FeJaW(qSZqUPn{zvEK`iJ*O4*7Tof5FZ~T1lc^-zT zq=@eHW4WzBMhCJ>u`6&ffWGY}ZthMha}@aYry;R&1hdQdMTm#WRI~p6 zm6ReN&wF21SSu=F=ZmOusf8iSOB~D}IFpK894l=q03kyGtIv|G2attCI2hDnDrLFs zJl{Oj-T-;%g*?w=`yE^Cs4JcykIX`WVFNkEpXWb;45!vTOF<=W12SjcNR_dx*>)>h zfY{C+oA-lu3+Kx%<5(&Hyjp(iqoA6WBQtWl&S)vbor*uS?-$_j-d?5^7%^|yTpQ6bsAV{HW^|>&7Gws__D*XJD+qU5dseQqVXR-> zIe($iX{r1F`res=pTZk6+uBraEM*R>!`my^`Ko)+z3+)7)1nGm(lN#7nTBL1(^QvASm_sFnx=QHr zNc~p4zeNAbb)3?L=yU#yhy{q4(-4qC*~EKAuQDZ8SU{;1Y&mp7AK~#<9MMUXA1UyG zCHyfL0KHE_`simFf`WtR?frCw-m^CHF9~{z-}VV3$!R#->|>dmCDg<)ezsZf9;gWeSu9G#A^kXXoHN}ZDXb_zIF6(tJbsq{fsm@^*j6Z$ zY&uxf9b2|tPm~Q}SY#wTHJ+e(;?^bO0ivg3(k=^NZ(mHE`N(3VL%VZy(gtFVaeWDa z4?{}mO!i98i+ zHV^`i?){Q^mH`}T^C{J+(|yZeCj$Ob^pXD~3^rTbZ4<_JX=J)*u?I{oKnQbbF>9!= z19_Tzif6GNnO*m{rHQ?96~Rk-_p_kmpcWwrC{R2`8dr6+0Y0*n8U{~BDOtXCUuB`P z%(RIS$Jna`%+RdTf=%l<6^oN@Jkcu0QSNr|%;8{fA(6yHLSym{205FFLNQk)0IJB) z;VI4-tF?QDgjX`+?cw3#_5BgWFQP}2ZA9+dwveHEXP)%keC+{o#zJWf(|lm0cil^$ ziN&TkPp|vkxiD$srQ?s>xBkcFDW0;7_@Mo};lXP?gCp-wFYw}hP9X4)T8Q|R2<&W37QyRh>T1^MaKnLj01j|s^~ z*zU|57p(;V*I% z=wq+!zIeS+`f|pw_?FVD2`N8CP`z%_guYv2{_1I)Zak=%nit@f;g^xz>rYg3QGZ-T zc^nD0VzjnBJ591jJ4G~k0B`N>x-&YX=aKwXA{3sRaKMP@-O) zpBAV%m>>1GR}!oEAQ6UINNl^zmR>YH-iuq>(u+fbDn z&w4T_M_00Pb-L;i3;(dYs@3dnxdC<>4#oB8rK;Tll&XCbTtGdaC7CCx3?&NimV0lM z0sL^9=hNJ+JpHZ!&$SC6@Qs5PGZP_E{{E*@a9DI~80d4GQdA~30IX#nC=Y$3teaZh zu&&uU=g`$`T5K5njo`3LJNRM#(O^MFPw9(mI>3>`z?=f}PY_n~#dueaQfkBKqR`xW40^i^4M-)z`<`pL<&fOm_&w^vkU zlEa;Vj5gp4{55P6+AxYvC^{)vDoI!=Xb9gjQh2}e%dBRdJ@ZdAyDntoDmAj+wc&x~ zw;10p!JGu}-kf({6&=$J0tl@hN;AI}(mjS|#0EaN;QB{Bs1|)|p*74TRT8pXcR7(SokhJNEIloyWM3c;dCIwrk$kW#XPcrn+qd;>uqb=4X6 zlmlG!Yc$lg;(}y4_bPCNi1V6Uwf0P^F+)g56>tSr@-{M2n~(d?(8=^8kNX@N^4_r@|IJ7Ox_)K`#Ghd&?5|{F$F5J&}{qIcO&>CE0NezmX-S$Ki>@I3PUF;MkHDD&)eu2YRsDo z)o@i2+4K})ClDoVmjSM!vci8T$WK?-WHF-NT%Iw`<~7B;m(Tkdlh8%+z*BV5?cRSg z3&^gUN3aE@NJix@nr=1T{8Ou1-Ok(T_4xRj+s8H=Uos@Y=C#-R&&OC2A^~}?r)JB9 z5585FfPHP7LE0bb*$MI^`!+gZC(C7;QHwl!HI>)6jTZ)(jTO>k*dYgTEZiaJbJh&l zrIJpsQRC?j+o|7=E?GP8cf^Uk+dys;vp2`0P^NP*<`zAaL3VJIMSt@f!Uu-HviByv zqoA9NM_bmjucIM+yd}0F_#vz!BpXBTE2K6md?e_`mx_TZa~cvFkVnaw729pgR;%sd zjXy4P!1C+qf$n5?-%dyDncY$0#Xn*MJ+av?$bMe!*YbT5zx>oqU55BRNfzs^v)U@& zI-Df~iJ(>ODll;pq5}II^8MzMBY*rCdOcU(rSTQ!>&C3+HS;UhP@dV%D16BQg>XR;@)hsmVQp~GmLV-Y!PV@7X$9Ans z^|3@Oi4!t4Y2gt~?V5C9s7Ou|2S?q)MRFd2ik~9MZW8WzP)B~jcwPUQ(viAgvN92y z3jEUL_BGmw;rnAKtXpp=BpN7v&-6Nb1`w?PUDB|Wxvi#^UV)6&tdsPMdp6pXV0h<_ z^IGNDs0^7_R&l0i$;|}zS&2+TbB(UND>qj|lXuZ=NbO_jXu`tma2(~b7{n9)^xe7r z%{|KDiP3=iPq(Wn^9<_S-BD6{sZn~dwiooJuMC>Apd`Y6p>Z>2V^T+z(_E$HzO`ge?q9via|p|(q){lA&?z+L1B-BW~AC<~P? z?=N}Ag8as#aiGGYg7vNOhh6}Tyt52mxeQ*m3|&I|0lszqJ&wCA%>eqD6NFe8WDt&r z*D%(FE}fsVIv!w`;Q{CN)Vjb{^cbZR2i)d>u0ev1oJVQ4CasXy8o7kHZA~Tt`6!LSTl1U_(QeXpO)#V!2x%Ra?p-$8mT) z-!5}3)UEE`4~+-kyqvFh54+!=C^H4GK1=hQFW$7>dN|Ynp1m8N zMLV3AjRa50FCiI@^u z0{w0JIPmKVO-Xl=UuWiQD19V~u~k%=K%~D2H5k(Cp{QiP+J7$vSDK2X{EhooNoKFV z05!m=6UDw*i(<6ve1**4OlUW#aHna5HKA-aJK2Wr?+5#|Est7H4eZAL!mlatfaYyy z9h7^a3FZAg?-s^7s>oGmu1hfq(0urI`}1y(`}WmlSc5U&l|H7wCb#~f=As}zc(Sh1Bd)}OXhR=jbT zxX|!bH@t?bjuBa?&sS1AORKFxr0eEdYZdn>f31;s1R<<56EG8&3Ih*z1YnuE-M0VW z*kSSgO*(mGm=UZ?Ejd7Z260hDbfPq!v2aI~-$A`k!P@K5v+lg0^lh5KGMr@SRJRb2 zeTozdi!Qe#*#NEh(J#HhGJU`S%vM(&s8X6*q$aGh?^G8yMa_a`*NNj-#63!y+N}IV zbrP8VD3C3g(4)T7W~z%E=edGy`gykqzkznCO7r|O6wMh!I%H`OoZ@1zcGU)Hv6EgKsTi_8hZ z)4WrxeNnW{+_B`Tgo{FPwtKO4dxhtbKavwkV?+o;X7-s5!N>mxxrz+;mV)yop{R?F z$l$4HX|by}O@NN&8_WBe>!S2jjS5)a()yhR&dZ&XYBveK*=siNs=x$=zpAn2!QvXn z!5l3fw~sMQw`jLzOm}E;iVjEimDh;F7MFvUkj8VhPnuz4PN}7 zLYWU(FF~n+=ID$M5G^Q^-)z7^0E#5H#oPt4GhF#Q&Pi&D1?_Qd=tks&i&piR^jZ;R zDfzzq_2kzyqFPE_ltxQ))>+bs{fl>GvGSsOTyOY`=(AV+S1p~q$_Wi}&imOqb9G7* z#@1++2@R%!lY^JmWbs3#RDB2E%b(mOTEU=86OIx@V&xhwBX^tx18#Vpg@kzj3J4s# zC{!I2*b1ibJS}iHt2s=%F>^X9>)Xsr`Md!;)w#;%rUbOZu)_zB{Ia&g;6;HJtx)$Z z$wlC(1Pea+mR=j7+JpX5>7*mFfE);@+?#t)idMi3MT{_GEo7Cem1+0VA`_RJ0&1_B zX4S%`qF|lPIdb5_W0$%z1D#C31=+>cvQ)r|gxp>{aECO9W&irfY z2=fjx#eIUAiNskenItc5m?_nS?JL|1;582?YdfUA2TB_5rm-5>EM6l3 zkxmb!j$mh(F%O`bA2g~VZhEB)IDzyE2c-xEM)SXb++Nx&79^^%-yTGPnEAGT|AXX4 zR{tZp@=nSrY9>(^SG$24KO~oC$93bJ4gqC_LwJeiKC{{!T)-O~w2m%CCt{K;Ct_J% zzgGt;Bb03xw2k&bp5-W$aG==ty?$*E7l0wjBs4EETF$>`;|JtMv-p~3Fcg^HTR z+TiNno&@O1dyao9hB7ftGK=hT41$!mGI&2eZ#P)FmNyXcB3o(P^>Ep&?^RK+qI6}fu>xWv>JxK6Hx zaP;IkKxSD!j0`a@!lL*3DxULf!AS~Pod2wYQUo|TcL%wQ&AeNUO8MYcD%G2Ys4fVM z`o`2N76?4Oo2Btkm-8xpWK>T-4P1lqUths=oXgbhpxUpDV7EbU(g~h%iHHha2KNqJ zgTTU9*@j1k1U+j?6{;j11+O#-p1i%`LR4)8%{Ya_1T8i3A9I<2@t>_5IBY>P1IVtx zrk$f~Ko@NQr<|i~0#jcHk2_IY&=+>XJ1$es1C!&!#cedaAxi$j!dszaEp+4NKe)1e z`Se1-zHfl29z4dHF_Y2h=TGBZ1D5p}q%5fqu&lqsN4A zPn%)Loo_@NqeF<`Qp>g@a^jBvBPHbpsN|PFjW>aVrCpI=KHMq-XNOUnY}n(!KB@6o zI<~_{>@oTS(GEmAYg24Px-_OxP2|tQz+m5@<1aM zA-S9#@YEpz+*rHlIv}U8yu>mw3-vmd{)LIh#TNC;Dk=<{R9UJulw*waX%|{I4`zmh zM0>R(DbchxC(7!kUVj`-t9395+Reoh7<7QLadD%6a(lz)Ep zNAAGA`b6DXLMks2sfiLOEkd{Z*N@y+Oz5VSFWzxbLYHpR&3+TQtfAFu*NEHI{DewN z*dUg!Uqa`;vFiVT`c@%>^n0$SU#*i3Pu>SctwpB z<=7W3)h$6m@dD)O3$2PlY)+{p)xqPAe3d>>$JIu$w@C=lMJ%?+&Be87W zp017*&Uq(iAdY(qyj;iScqdQk^*&HomW}ay&Bcisc?5FuU`m^@X3pc20e|_sod2$g z!h<@^yM*G}duVW4-t%S+r|VR0__MAxH`IU1LsJ+hby$$@{pwzztvF;K|4| z(%sZ}@)q6Fm`kNldJAKIywj?rL=;M8tnzwgA^m(WU!mY;_cvBSZ4x;S<>OKM*nvNN zQk*W82C)A0OSO2Jp*Mk!EfYW%1Ey!t_m*JMsv8VX=wNfnQI&mODTgk9ddH!Eh;_w{o^j9%u)!_Lmr%0viT+VJk4cH`Txb)J%(4Z z?(4#jopfy5cE`4D+qP}9qmFGm>DacqV|VPFG3VTC?S;M0KJWXfMvWRD$5mC=^Zf4n z?|YZ=4CBMBVZ}xAJusnn%jq~P-2N%fwzM~k`I^=uk9&`fw*vkvc`;xsD9gtnwZ&|n zSrL6|4Wm?_GWzhAGJ4pVX|Cm!3iIkW`{x=gb8+g&qw~iVK}@!ENKGJ{WDlDiM`?2Z zY}W-(9$XJ>H`DEiXZwlUZlWE{SIRB<{2Hs}Uz5x79D4;s2Y8j2l~syZM{S-tsdUQ_ z?k?dDD5Gb+YG8ZmJ_U?H%U(}!uJn68yAK}F{vohWzvvDIhqQUaqaXKv$|7J~4Gwi4 zmHv6=+vY()zkM2sEn=vt-^g02foxK*4VtCYr$)*0V94|*CTY_#=fAs7K!h>w@ znP=+kQXf3NvgaFlBP=4%1by^$`bMdz%kM>Q{us%J<-?V@RjSRoM(aY2Sbdx%I3C+4 z1J`SnM#VA6MAWG{%9e;G+0NRB{Jy%WR)B??!U0>~4Fi5OYTHwyMIpP0XKBr0dj&JM zd_D2q0*^Ijxv(l&{zeMbIIQMOmp%_9y2b#hq9yMF|J5tBt4IJ|I z?|smP(c4gDvZ9UO>_O?-D&3u1nwEQQTx9LyPgvXWetr^Y*B@+t3CQpNqRgJW>I$=J zug*r8&5EWW%h+*kNWG(&qR5RiDdw0Q*evNs+{OcK*O0eA9gR#a@+Pu9N(a#(et5eu z`Z%);sd$w}4g2vM<>l?*7G`&C-Sz8wwnKVKvOKH7F@DZz&pF?(ZOa3g;(x1F7pH|RVbWLONNfqlt zagUtpTX8UZuUTae`YqY`-LMhz9x(-Xk#uGRv6&Z^8>1ZAXx6~N(hZHr8{u`G6--k} zqTMwj$dl_6D(3zoCr0;KcJd3TKE~1vsx)RfLJ1HEsLfByL z^+9s5IZHwM=`Xzq4ZH2h%2|vG5e`)Mu`RZY7{*$6P`|Ur79(1;$41#dbHdxsl$~*| z9<&aloj9UN*&v#ts*ReoP!JQ&>kC)p#ofvUKn96Uec}ik3?$>Gy8e3P?CgiUh=1TT zUhScb1mkA46LdrttpnK~MRXmnIFBnf(VfsOuag-d6ZYgL&eNH`izY-T7EBaB#*e{P z@=ku%ztqf)7pO&I=#Y4vBa`e{EL+%Snl%srF8I=3@G1ilU7{%q(MR5&{hEz}WE|dw z@E&du2}mI55a!u5D<-FW-TlPtzKY8ZBLc(31SKWcC=sdh05os`9C%8$xs5K&o?ueP zg{&K0@T`&Ya~Hd}6?iSumdEQQxsS?%%2~Wo_qU2Ixm*dhYmGTD!9HU)7TLm*x6KJw9b z-WKJnipJVUzj$1$r+YsM7QGc9zd%Pxq#ajT&>oXKm`n1TP2AsrKmT0m_4_!x1|s^P zWaYt(y3Qw9r1)C0USSN~S9h)dvKhbiJ>K=w-Pr1T!G(fjjF@QmBtT#%S*aQ@OqtO# zBU%_mNg4`=XeQ!n`V5(xBKe&MqiDV4li6XgT|qppBZ@%1OH^qp677fu*>Bw#HTgZV zA9wBzSz0)voVRZc6+!weq#d`dg))?~mxg|-VbH2hU-#I%JZx+Rr&vzj+kusLE9xV_ zw~6R)IS4R}vkG~_V17HIC@2*Z->FcS%7JwY4?bDDCw+UY-q3uFLnUZDyT1h64cQ<_ zHy`QABbj`ptBtefAHg>Qe&y2GPvaQr&LgQ}H{O9fKZ+O*GnXC$j6I|Pj6I|@vL4Ek zO7u)@E5MI?G)lgO3Sa=aHaGu~j#=$5;G2l|HNR)qS-{tkH~eR#@NdmmF4R1{s2PMI z^YEW66V8-;w|`vd{ruSodO87oe-QBEB;X6}fl$T(xrpI|fMI`+OTQf+&B3M9^#$W9 zHxK`hGr;2o@Ia}gutF4;HlQtLfWT_ed=F9_%g<#eE2vcM!Af>^JSQC6dtM?tWd1FT zI&NTL(4dgC&@l`$Q56teXp}#TRtkj(Z8&YAfps9NS&tFqBHmIGu)kbIEU5|YgDoKr z4(q2Isvdxeerz%;M3V`}2v1Zw7DY~Q%){6>HhjyJxwnRTWyee7=Y*6Oks#Nq5fE zfQAjJj9ktBOGU6Gwake`7RfdB??WoNYGN{K`H*;3LSZyM_bLhL?E&dzP`%{kCD_Dl zUBHrARwHqp3O5ZaJCJNeLrrtwHeioq=7z_H$mPOhU!ekgt`z$Ld08(%Zf&02m}%K6 z$ZZy^%T^$eVPuZhiiuiLPco^(njIEp673{o(<#Td5^GUn3aluUifMMF4=0;YzP;Vx zv5v?gR%$N?j#dRjpmXSmPKLaf+rf3{3mT^}^J|GxC4&M%A6imYR|{Fegr zSSE{dQP0$s2wrjuXlq&P)h@ZJ|EL-bp*Y=XnBtd}`e&#wS+fvOv32?GF)~;*q`p{I z!Mi?!jgDy28g3&{qVd4LxEoTVF-hkV{kvLueu$3>#*FgA(dEKU^2SV(`Ni=3+@cW@ zUVsq)VKoN!F`wp$ajO(*ifQ8tsoN)1t1Eb4(7)_=En0r%wK#Zs^6Qq>CCf1V`m4a< zNT_usLnK!6l7bnu_mx>cRIpQQ7Q;k?RHi$%)ZixtTW>^jb!8NqY!6fB6!^Hp&)?Bx zuv(Lu)JlW%-b31)#?$6RpYrnad)E}ctwlbg_FNAEhKnrDDx568js&3Xxd?VglrIBKULcKDfvz3E| zJ}N?7s>Rs}`hK{2(!69M?T0$sF_c_;K{1$hR|D*4pN;jS`21HC89c&wLYZP&_9^gL z?83-4S?@4(iN%BV>#6%Q>%ecrfK579bx~nhL_4{Hx4uJ2N_NX}b<)MN`S0FEWK99~ z57j^}(D{spAK6uHQ$e=Z39?RNl|F?qMVrxiXNZJ!#l$nM0ZXt;Mf>=1wgMR`BwA`d zH~Zuc<7}lPW#U(QdIsuqtYj&h3l-PZR73Uf+Td|#4#(@cvm0`>J-)++wEngIGiQm| z)0>?`lYry!T4yl=5Ay4(7oJTQek6YGHa@;yyaJA}Jt1hVct!sd&UiNtI_6fOy*oz9 zRsf|I=ZKxfl&pWTtj9HHS<6qR3Ton0Dw7U&f1myIAImzul zO9g~aIgoJt8pOG@yI5W}PQGjs&Ug}@VBwJUAo?udcAS1JtonA$&R?xxZy3IJ$~iD_ zvM^}1qxK{7n@$gXG|~m#(7GX=kJZ&3$bd~;?5jOBkOi4yYodMdL>fd==lw)tCUqN) zU8SP=X(;gaIu;UvFf4A$4|d4QBQx?tUQwiG5WyLhtB5HRzQ5r4+9s?F(MJVBSmts- zxB(pm()3X#boOUJSwlb{*vM`WP|h&{VRI>JU)E0oi^lb6AU;ti|GH}2Z|nSP(`OW4 zCeHIjitnYhxn=4ALQM0$f7m%C&6~6vDW#PlwVt4jCfMmd7WWM(F>s}8nvCAYR3OXm zlUIKG=fH#3G4GXw<(c>Iy?M8`COmrEaOsV``4+(K(>8Y*pWc>t>liPel6cMoMiDLa zxgiDf1#Gb7F0mxdut?4L3L(ru znPLxbwA&9)^E$g;rWZTczI}T&K8)#HJO44FtbxP+%c!%26`a?QCb=3@GL_%?b22l&FSFghW%B*ToRr9X@@86)vRul#{Gb#PVF+X_69{G zbMHLm^{9B0f#krio$_&9sNMWy%$G%;58`NFZ;kwMLh1%FY|Mc;XiJ04{V+LZ{b7&q zTHs+6s@4rtQUp%Ds3#vHMy2l8+}&n~hqXszTtj9LB#-Me0m&Nkz&np4t8pTjG18(D zlMGgYi;<#;(cU+y_^a5Kr0`5@B7s8$zFtOAw9$Cf@fg&C5Tn(E?;Z)nmUfEb?6<4zMw1bWE{3n2)OPs%EJ8AKY6e8!RQO+>$QGDz7f~&}eUG0j z-BC$9L#!%!3TIpCT>r&yvc7(&32$fV>7AZ^BkXNX6sk`(xLH^ycejnS^OOmOY@-YV z(ruPQLJigXBVwU_b~ua!-jvg5TFF$t$Y3iQmTNi-#+ut!240fc6z5%Ic8{xr`Evt! zfmg*70p1|xqKMpn&PYV9{`4smLPUI^hVP2`)B)-9;rEl58qJm$eipw~RsA*-8i;G9 zvywjZx%j7Plv2;gaoHTFVblcI7*M^+sE|BhDyeBItH%5cM!N_gf{hp>)innzVh{{H zO^34lNBwi^y<}>r)9O$adcXz(J983-=!p#iTAR``a1l`om%iDv3P$QpgLyv>1*x(A z{+SklA!~W?omT8oGnc$cN>y*Fjja3~b|&lMlZh7f13VFvJ}qw}#8d3Uc?tv6B&n=6 z`hG)z+;K3KJH_4o_s2IS$Ki0X#=9S*UDyb}VGi&H^~%<{fo zh&e`9;N)m1Pp)f8OYpii!$Jsti8X35LJ4@t-@9Q6_34gN*ffQl=Qs>2KA_fmX#jXS zey`Ub-O-$BTdw?&=h~E-etDpNWC?Wz32Ay30K^2(QjW zrk(zzsms`EAivU~pFyes*h}=(Se|5W>5SCSl~?=dIc&D1z`#DDq{d<>YPe7po*;-Z zw5E{vbl7N>d$NIwyA;lQLcKsEL^bxH76(QwRq=VL7xk2x6NW8Vyz; zZMVxZ&yGl<+hMx=)`s$ehYIAJXE+u!O7EaVi$w{&ogpiA3b9+9pvHmHiD2Eq3P10-JF|iR~CB`DFQ>Q}^}Pf|TZGA@cYB5kKw5 zR_{OWJII6U#w$*uDUPCL zonxr|KpRs)*6A}vHw&HS!xUEumA6u=M+=5UYo&=^E`g8EH8(iE>!(72p|)4t4!@`o zRf}FWJfR}k=N~v=nBfqvq2L=B2<>!EkYK$+Vl~Jq-jzwS0N#J8jK;8<*xK2Nuhe8V zvRqCs+?N=!Un1`Y}RE3w+&53{B9M73}UZMla6l&*u~h|pczkWRg=JNudKD-9O#(4?N-7F2` zpP-VseY543IuJ$~K5`242ECDg;sVC}Wd@!Q6Q2og!Yc&c@_W}K--=&d)iWJ1gj2jt zon+ET^}$Ny_lj{wRYeFiNdyhGYHo!~ay@HU6yk)PejAGEbrB*uma8 zPvjsP;g((4`_-qWV^ZJKmtf_h3i63bcP6L(W#cusyXGh%6Zs;nA zV*bZ2CUl-v)77&LAdByr@c3N+F7sKbBb+mYr`?!eiC)A&b-f7h#$_rxKDP&lF-mViU`4(l z6#9#VE2`kX*yHY8KW@FkTs?K{bCX6P&n&BJ;~-djX;Fy>$$s&xY}vVQ@jYqjXFMBc z_SOZWdD6lO(>AeDt)gF19k6n0s##&3;YivpZ^^OF%A6Hq2W3A}NK%{dq1O}{{aD?Sg-pnaB8Q#hRGhZpyc1c^9Bm)42UHzP*lM$?W*$YQM4-FSH+I+E8sLEJXb=hA zepgs0MjpPv-&jD7OMK6g^f0%vNF2vKyr(W+&J~7X>PlS>sd-{m_Yt|*R#4MSY&Y4g zKud~~UC7el_6h=rtsSiro7PR1Pv1dqkvOKEm)_$S>M&3o?p8IL9C485KxYe#I04g2*2CY8<7n!$-XGEHL=?WnpT)@WEi>Q&bg*E8Pc zO0vRdKaSeoSblzB^NMo2<>SRJS}ZeIIHwhDB~(tw#Z@@IsgWdS8k44cf(IxlcQIsp zj<$3G(8LVyY@4-0R=(nZ=_0~kF6pU%{7o$<@T7d`7fl3O6*DwEgE{xflO!F)XV#QV zUfo8S3a(*NK2S6R?uRL-^>G4kW&U)4>iV#QwwWMeywE0JZsmzlDXK47abW{(z27Efk3@C5N z^cy@yl@$tq7WZoXwtmG?n_A0TJ$&S?qe@<`-6k13J$#h@a3)L4GXyNfzP=~b{0PTS zyuRmfA=^P9z#jSdTL8rCe*2@CJ{luA5dXVj`~FwMC7UjZ%xbmX47mKQWMIqK{^+rQ z)?j~~)PK)^tp1h%@Fh_jbrgiePWwpCBhaEuSmJ!x%vFw+_%QU5RZrag{w{ zf)#iJc?XWZPeR{~J-Ar$7cMN{2_~OGt6;(v3J|Z(Cg6d z|6L5ml}(z-d|ki3hs|PxpPehfqjy9{wYaI^SNn_H&N9B5$Iu`iw;3EP;{Zd) z`P!tS`nVTa!}bvLJY8n~p1I2X=-1aoEK{=eySiM^I=7vP)e;u=TB5!U~@LNB10zO1jxpAX3^T?_z>Po=6&ZZzWyE%d6G9_4&Uh*9! z-RU1L46p&(q%&XjWVMFN|2{aUzrqSh@u}TmqEF`?r6r?WPN7uh`eP1MQ`3V5{{3dF zm84o{c!$^f1@f1@{3BpSh(c`(1A8t5tnkWh?0EbPH*yDH|N6b(q5XWvRBaUVP!3qB zL0MyUqj6QC?jgGSo;1Psxglm}R}{j1wmty2Y!(hgpSZ(+3uS4F_e>1oiY5j*fCU!s z*5S^&ji=QH4cFFPIRoC5sJjt^t9owl6Xw1VQpTE4)uTVl7zrEK@)^*#39+l)hIAg; zvscJGpbwZ4;`fX`7dN`}JhdXchlcLIyNvmXdH0St#nO6kN6`!gCV!hanZGb${4I6F zGIMc6Qu7+e#rJQCkVn&dBAP=IK0lI8(t9Q-?hmrJJc@3FXR9F#2~<)nO;)Ady>Wki zQv>GcOjj5-vZ&D-=rB6i@@%Cn&YM`?Yu$*hRn1u_7yvF%7dXi7i!j@(`8hseG)=8k z7h>HyjxPR#BI22*?`{)kVr5^005kd5n8n+>|K|k%`)w~Dm)Fa=`ryv{M`$N<%j1*A zSb}DP-5fBxvA6W7-Me@o_z!#G4+V&-){DBT{9~sOwm+M@IBUo6$IUq_?xs{T&Bx5X znGZp#75|!4$6H3%MJLLay!X$YVbe{S}b5E99 zIhpXz|9bY5ibg;jj1!C(ZOOsl$qz z&6`8=rlzHmMF(h^>CjWH8^Ks^ikz$Q9RoR9$Nkow_Glf?FfhvwpfcC!WhgPMO<9e6 z2u%}W+kK7S4VNm6G9q{fT@&oJpi<@?=bY_r$$Ts&zLI3he<0a|GII_3-|q9G_y!3P zWd1bW5|sgGra}71*J|)nIH?B zEMP4oYW4ZB8|xS)vdlNPkAOaWPqtufRwM?eg5x~MAZgb+$mV+js_-40wbV*>YNiIK zqf!m_R^camjBXIqiXpMiE%y_-mH>YTOTwT<#Bz7Z?Y>c{FUj5(s)S;tRn5Fu!T%NL5s@hV9J#SrP% zn=@+>sh;Jht?=28;vH2C%&uxl2&zU|v0I;oy@T`CxrhR8bODwuw zpaH=!YF&@e&Ac;^c8;qgen{+L^|kR$TmWZ?=X~;pprV71`*iXKM)fF@yV0SLnhZqk zD?ZN)&~0`Sb-Q>!6v7;m8E;;GnoH~23c2YSvQc{=KxhL8Qnx3N;bScmMHF}XBEOgu z$dAn?&q@$J-~fRuXEQe9%)gk})FNnz6q?ni+3#Afy5F?OK3)Q6?>@s zMdm-E@l>-LxS#fR1X($RHllb}lcV1S5#Dc5@3AvU+kjwC>Mb~7)hoFKA8cYT= zlz1A{i93aYc_{baPQ=PDh>GsMp#8Mg%nUs)nHDr_TpzuWkTt^MwI?sJ*{W0JCI1vm z{!%4@*49Xb{_7hIooKTn%bdUtysYh);aSxOb71uyt*vSzzX5aL-X&^i^y{0qlmINH zW;E)h#k8m~_){U9Kqk+bm0koTa#P0twf}l(NPwiSA<6^USAeDq^Ck#t* zzD9~T+AyA9ffjOsvBV3QHGyt{mjotQ$IsNS?3*M_n1CENV@(YHXxC~m8a9zC^cp;I=mTSXT3@56bn{J|M%%#jdp|b5q%B)mUis5Q9pF%7XZixRD-}cJ3lWUu%^ZJc~Gf zHRgkq8#~sX*3rbBUm`P?IcCuD(_;K2<>1fGtOu%+`Eq{2>WP#!f!<={EJcHtv8=2E z7T3t|?>koKU{)ajp!xxUIc+FJb_Vqjj(mbY1hD(pw($AL(8;?SQ@n( zc}=x9xf`IXr8A~JK;4Jh?U-*W91iTreYeM%n9U}K5G))bk;Xh+MxZ8kk#`2-LvCD~ zi?5}`u56^2pCkv5R;pZ^n_B*?&Ju6Ii75Ux6Xa!kH^*M&xRuuZK+n*uagDsKMs$dv z_CDvnQ7e^gEY4LS`9BiwcxZtp_hSYD_35CSi6>j{s%KcO0odiIkXt6S0gBL;EVk_k zX-^H4tWz+*4aXZrT9r>qeGkxI^bR;xu2(_2pl&wqnp@VX@mhn-yX=Y6RDE~me1z3Z zsa7&pP!R~;Z2LTCi?l~iTM%&UT3y%M0+1flElm2?oRt=4p|7Rjp2UX0xyRVL?L@AIBXlZKn^Lt#fRWh5?lc_qq#Pu0 z@S>V_^MNsP9I1K?PGPZiAraBqzAQdw50>V!8g;z8zJ%8$%gJY;>aB(F8?y0L$iZWU^q5Am35 z2g5O3Sul&QJHfU`E}INd_teSB=YC=@0)1WqULyZqy|uWh@kjMmx@>5Sc3-?^ReIGp za*_q}LIbh9nEVfL0*xq~C{HxC0>F(zGevhlqbpT0cd0`ntGR+dbz-#z%FDN(m(QTE z!Su$4+3zJ>+yU5IxA$wo7}nTWAf??~wyg|93r<+}_1n$@2o>!gXxoJJpU^hVCwQky z2~$^$26zuE)$?|pv5CVk!3Pt{bK}rjvO%JpR@We+8qiWuFEH`y7>e>6=swb9OYNNF zjDH~8LHU~1NFH{ia#0EkGG=fl^z4U#>Md+glClcCZ@W?TC0C1QCcw}dYC$>ZcIZ%o zx1yw%0)~(t-l~Mj=-&~z3fjHFX{BkyDJ3)o67a#aeo!@Zqwba`iG>l{wv-BXYyZ7@ z>o2eUUG2~2tpHAk0YE?oC(XX=0*#ES=7Owod7P)Tay_tqWsJFE!gPhWaqQ^3YZ+d@ z`V;wgco#uCSYFgD65WsmTQ?7iz8V8!noU~UJQ2-osS!;fc&Y^DFx#f`cL5qXOFkx? z3S#`(z$Z6kK8^B^DvES177u_PQZ?1$5oTc{&|OsqO0g14T6}7bI!_r4n<|X7ecjg{8HR zyXAnz<(vhr?)Kx(lBO2}GB-){p%m59(PNe6gq!FbDTaJBxy0Y57~S76iB2k)QC0PO zL6y}zPs-&I3oI+s7F7TxQz@$6S{2@g5Ji<8%1Sv#-pZ=p^NKk^-s9|>{jtkPxa&Ueg~{ZS#GA=EVqYPR;~wH2dW_NLdmsMrSCaw#zTTF%Jh6S}i}c zQbko7$>_3Z1+U7ac#FV&cGh_%9ZIP!;7y)Mm>N8&amL~`h2uobtM1&*%w;M8=`Xo+|pXRDrGFTL~kwmhUYqc4u9w98Y zyo)ukw~N0awc?kkLUVamJh+iVIzrX$cL6&^07;dJTc|a$^|ghUxpF{ABF0ZCQI}+c zY>_B?)rK<251T0^)zHM!M6p21K=)P8�xFv!vN1eT(g+6UFN?X|e!Y|LVaJoG~wg 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 D5kYbu=oQ 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 gM~EK|;J2ROrZlA~O(`h literal 0 HcmV?d00001 diff --git a/assets/dynatrace/dynatrace-operator-1.3.2.tgz b/assets/dynatrace/dynatrace-operator-1.3.2.tgz new file mode 100644 index 0000000000000000000000000000000000000000..fa4074dbe6ce467a3729f9b46ec48a93e0badd75 GIT binary patch literal 56455 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 W;rlHB?7#mkpq;JKYJfW!;Qs)f3mg*w literal 0 HcmV?d00001 diff --git a/assets/percona/pxc-db-1.15.1.tgz b/assets/percona/pxc-db-1.15.1.tgz new file mode 100644 index 0000000000000000000000000000000000000000..f5977381e150d137eab8f8202486f86b22b8fa94 GIT binary patch literal 18563 zcmV*eKvBORiwG0|00000|0w_~VMtOiV@ORlOnEsqVl!4SWK%V1T2nbTPgYhoO;>Dc zVQyr3R8em|NM&qo0POvJciT4dD30&ndKL4L+h3hrlk!92W_>q%uIpsm+$OC*k<;#; zZjS|#kc2fwumEUVjru$HjodePU&;62i3Fb{Qg+fj2zxfQNMHcW0E3yqa3;A8yl^}? zn;|Cr1)4{Hx&N}izrTO{=#l#G{{DXXzXykp9{=Uw(bxOOkB^VOKK}YI`v-?dUqAi} z?7xGtR7%PPVt?7cb6@4jeIy0NF%?J<8uODLK!5~_=+qC85E1qX9R!#OG9frTfdnxT zV^&)&Bs_`G!i&)ypTIe00gVy-Q!w=G8#s$nE*ctMrDN<}W5x-MPvEP+KRxgd54`=a zYAZ7wVZ^cNMX%i+O2|*P{q>en97g zkOpd|EXD$JkD6xQgfcI=b>I0jF^_sAkVBo22%iiFh;uCXKtAIGJ!wCQr#;!|ry9)7 z5s3vNF=qUv=K-E05}kmcbHXEZ?I#hs4oL97obmMlG)jeenK$E@P4U7{7>!9>+lI?` z9*Ofdu_PQp{v5INie8goR@*Sn-}ed>y)M!3&)y`Ta54P<8bvAQ@8|$n&Ho?oAM78M z`Tz0J{+9p0kFo>L@C2oi0KEhhN0f^q(7ey@?ZCy1a3CBI{Ok19OUbk5NC+G%{v<0r z!$E`?hHJzK8b_FepfJXeNX``ki3NoPO&JJ0Pa-6_T(37FF^b6Vib(-@P+*rh!U9Xq z=p{7d9*I55Ld<$QJ21oogc{LA0)-nC3)!D;0|}$I3;tUq;c>zRjs;xPD4k;tGzK#* zU7OIjx6{W<`>UwZ&M-~mB%AI;J4 zG)6aE;epWznMqx@EssA5ogq#uO||(K;+y`px<&zCwdxv)s|sS zQx;&ZLCFWdrI?HCH^3av6~+$s_g`fXgCtc?=H^L6=A^FCVWrWbZj{CxP4GV=G&QY+ zoVuMQNFFdGzvIFqxh2XE$0)c;lNXaW@eHF#%og@#nuJK;pEZ;2bHv0O^{|&F zQ-(tPW0DD;=Exsnc1;5OJPs2|Vv!3}!3+mieo=}*NfIqiAS7Hahp-30gBcFfND^Zo z9>GKTzrI;6XRm)k5ee0j_!*C9lwR4P%BlANbiMEd1WU160>jfE)EhZbPqD=VAK#*R z5@9oj{LNwGo1@Ab!g-3>8I30vx)r5xKv3jy98EZx#yIqJRa5JV#Nmk*OJBHeri`Y^ ziQ)sEVG2H>yJkLC_1MR z2^J@C8r`4;m()oGeT@TpjV1gHrT8|X9NTGEQ4Sy$2|j_B6oucQ2*p7*M|>+Vi&1pJ z&}2da+v7_bpvY7q@!Bep19+(R-fI+3u|wyBeP8`=@T8{6i}MpWIQ&}GI56)^N|fGi zF%~zJU6FX|Pp_2rBEfiL1IN?T?7uK0?%TdJ6Ux9y9(1Dq^$ri!Q-+gBa(Y7Jvl)ux zthxu86n$_h=w)&n99q0^Jyi$MPVGZe`=C-=?+^Vnpz(xEQ>J>(2dMeiYb@B}jHYtW z9+lpm(KyC|P*qOgct3y5qhc58(eR2$4US9}g=Hc2!4vpufJHEv%aeJ)c{B)+A29Ki zsbhHQOTtA?&Kc!G@=E`&kNv~P2mZnSK?X9ROb+;H|2TVegUNKJ8t8#qp2B`m=$bEr zlnXldvgdYF$z3qS0mH&QLkyA`xkdte3{MFcY~ckAhd34lMQX2MaoxbuVEPwTJN(uByx|Ndmmwaf)2BcFPxBnB&Z>HJJ(*@>wJK^~HC&aj>ilm)~<7@94F^_9P z^$I6&@%$~FzTftoL-bf4qRzRiDIm*EwZ%@M%?_|M7G#bclBo6Lu|RR^5C@3aC1ieT zS*P^y{HL?hlh+M%vBN8jHl`sS;s^&)CdrmiNcfdzO`M17k)R2U=yWkmM5!q!@QnUI#n5bIxeq53Dc>zL`cN#$sd;kYcGIu#r{B}mM&Y$& z@!^U2txS36cR zyeUHgKF5sEP(4%YDIyabEP^N#WfF#mf+qP=$G13e7rtbfNXl=&Kz}eM@jxDKeXtBL zp7M2z?jut>;40Iy5&zl%sBN9oR0c9{J@#!^+)P{fn z#ZqtQSoHJ`c5CZBr#PHyW1mWVWLj@vWXrN(r;ZJ?#g(!wbxErzYwiX^K^dB2P>j*G zc`<a^6!#MxuKKi@UhGTU$^}T*wvwnXMZe}EyS>=I4xQNl51fUKZxnD>; zb>OA383pW$8J0srX(S#<44SUuG>Y~hrj_oLR1YMZ5y%#M5X>mG)?^DW?;SHAV0W4v zN^So1=b@YhibKRgpm7fA+wY(6$=C4zgC7$d4+RRY9+=mLSnhC6BtkTA3j)mfD=KyS z*BFIAGa~ShabS17B#{vUb1tT>w)n>5F-rK13Qu4zviDj$(Zn2@8re&fkZ-k)VjKHr zWd79?xtUc>t*1Qsmh{xJH`Fl}dxnva2)lSWEE+sY29ih_PB0XX4s#Y38%`LV7j&i` zsGqO&ugs(%0lb(PYYbNuzadcsF%^1Pa?dD%LW4B6G!%!r9*McYC&P#)k+f27*3)PRyU9dH@om)KU0Hvnj{gH;yjAf+k!4^s&Q?ZISm4-r{E0x|}Z-$idsLsk#Hce!v2;1h+f8sx0V{Eva0=83DZeiKm(N;5wT@;8EMH!L{ zRh61!&&>%<;}B+;;mmHu(2YMOVwR3muI42VJf+OFv=$ELh{S_&N}|wW#G6KRj3Pgn zOzklB6Km+3quYSSF&yqcdSahhJgomXDkOGg2eu2STKUQkHoggn8YDGJUKer z|EoHq6QOS+s_&1R=B~A5IZ+WLSEz*J%P|VBoUWy8&6~!hh*mqgQb8+>?8m3Ar67@D>szW-yq`eHC!{p}O7|n@g7ut{Ibw^-J*#`$rkS?AO+H_BnQ1t>KgOcGA&;d$z}R_IEHJqzlWBH%a_B$yANM!0!qgg9$Xrw7%Vy3gNI%odj{jWECD z=o()NdMP!8r;0PyHqmyK*3~7}TU54-sVjJQ0wI}9_~jT+D8rY05eH8XR!^aI13ifr z)hp9zZ1aa*pk?BhHqD&IL{N8OnkW5o4Q-XU)Q)|{)gl)_ZXlaUQH(!5bPSM}#+IfW zHe5rbHGOD2hf+Lxx?lg&h)JritV%+oD0_40yih|c?dqeW{o{TaI1grcj-K{OtX-K2 zjm@*ZWBUxQw0p_^qbHBQ&WvqlR(1e}D#a06^vwttlr5ef*S)lW3M{5lDOXV?*7%1J6Btm7uGvB+Sw(iOcdVCE2R ztbU~xW@2SK5%JoFS9r0u-4$L`J0!^rv#0&%XV1QS?!6wKdZ*8a2Zv9*Z_i$N!|zTH zAKMl2$A{sfwZ_`j;4rxrDDNmxZmozPxE1j;G6LS&WV*E?Zmo!0E8<30#CEQ0=Udmc zkHd8>_aNI^jqYkSis&?;QGULbcSgx!r`>mNo(t8RL@AENn200)z<;!=?kpmA4&%UEv037`F&_6hS;vf1)g}3LF34p&I?(Y{KzlcM8 z3-iS#k$)6kT`UqTmGes}=5&zo_V-`8GTvdWjQ0U9^1;9w~Q_5{=8pAAu5A z`Ou5{XUeW5XZh-arTDAtb?toW=Tg$#GpV|u@w+-{t;TGR_KMc*bX3%wU2JZyJ?Cn< zqe0t;?X+lHyM-ogOkXuBYm+K_H)VFMI@?B}%~V=<#Z>C7(PNu6SqZ+h`dqcusc-dG zcO3l!JXc5kE!Ceb=Wo>rPM|M4E530QSWB(ELdjB@Ee^~Lhqvao&>R$5rKqx4S8lqJ zIq$2?@7O-itJatYzbe=V^Qx;3@r-9W2YG?1)XVI)Sy^t+SWZ=ThhJo!Jz4Wm{d@Fl zb%ajDg-ltGuZv4SZ|t9*zkczLe_dX@9M)1;9dRy+5u28lo@5EReBl~+`ku$>98V%N zeTv3Gai!djgNUYK$2MFrjOMKy@uR|;NrNjaPGETCeRF#D{hM>|n>T0QKfmyPIDPfp z`|kPc=T7Avc*8L`GJuvF;K$eQ!&7y#eUBG|O3m-_!U0!#t?a>;k4jot;mA3m0Cwv7 zI)T^E&tIONJ@L``y!~PA4 zLwdvgxB$9nq4|!AULgvLV-VF`r5_>^Eevk^6~3(Ew(Q%uE%xmd#)p02aN8 zmEA4D0huh+Xq8T0`MOVk^k=W=Gs_I4RGj2r?Z}^=tz@b=Y&Wl%KP*=_8nIK?JSkT+ zLsV`6PHp#bxryV8lQ+m8R;thES`~L|+h^s<%Cwoiy}}FoVJ3+|%#eUUMdPv+bd-wh zqs=h}5(iNl;vAj}>_mccI!6iduJFQo_GLm)HqPu)rZh|^Sc^-y{Arl-LUSQs;Ot)Wb46ww!WS_&-YPwdd6N@EML|=l;u*Ex+Y(K+0bSw8^OP;ZFY8czI4jNLYw8~ zRPS2g>dzX48|VWV{2eagZv)j_VLMw0`hWWG!qOOI zur>O#Uk{682l$&F`@a=bg_j3*Q7A&&zw>1>7z_pllsVwt;j5+aZ?iJg>Qgiw$iDW# zEED(No(5i~h=ZsScAAPB>A)?!<+8I=qGKa0wNCywB{BT3LR{LbC%o6|sTbDgV)$E% z!vBJAu>g!%v@q8O%5wqVC^;t$1WYgzDZ_jZNPJBM_LOJI|NB4x7x62YV?gmj%t$=_ zfB)zIW-hQ=HM1Q#H>dbE!A$Ty6~kptV=;>sz;DnL7@ABX6~XxLfB$=?G?%6TfBW0t zdc6Z5E+|Z~00gfV!~cG%KCe!sj>1WZwMr7J#rl3L%s^?*!ZhWPh*0sc=q zN_TP_K)<3f_TdjL^uPY`$BWDJ)8X*vA74LvI=V{7I1mwdF}!^X{%ajV-SV#knFH{`_^h{&{u+r39{_l_{4qi;g3I5(Bysa0+5*TJQIh|OXZNjOZs0FP|`Pu z#SzE%hqG1#we>#@{KcU}U$1wlmbs0cfnqpKGWAw2j9w^X!t)#9r|{b?5%nhJ*7whNToP zp8kNZF;mMoUxhO{SF4|_S;ux@O(^vFbw*AwTFwF zb^?|lSQ-a#e5C#)^ICyWr*J$FBC$a{VB^^6$1uXV>n9FQ8k8SQHP=EsCZi*sI=vw3878Y@`5A0Dj0yF3}~ zk1y05PM)?WtuxLH9U4mEV9Uo^c-nb@2_vzXK>thbeaZVJXu984FwJ6X`n#|Q9qP)B zV^Um5$|?$*<)UjTN0xO|R#p3rb8=Cx|2COZV5#n*q=?Y}AARUw_B&11Op4CuG&UYL zYX+(-X-G|;V~rAQF7EVZIGX!>Hc%5^(ZnD|wW;=}61>HM?z2dFXR~OGoo3nN>S1N6 z$t0*slO_C?Qjvi*vQ-s|zRnB1@uD~;ll}y1381d8CL|J=akX;vg{}>w%cYJqQ#U`u zBiB2?hZhsL!4P60H7ha0^F8_R0;W{Q?dLc_EDvJ4ZW-DR^Of>NHU`N8NFcH|48>Cn zUtHnE9(OX*8{F`~?%O}w1NAhW@a^c~wa>p;e z>({x^bZC=#&h#HR>!?%X9D@@?qM8Z{4XxdZfbjS$p`61g6FB^@ddBqy&o$YTr!E`e zM~ai|u2U_)c%Vq?Ks=I=qzJJ~=R|bCcOy#cu;q?C>-#cWR(b!$j`>kv4KZ(GYv&7B z&Bwy3KjRu(l^Qji5qYa62>LHHj~Y1bKhjvoO#p^P%3`j4)ZxwR7l49*GVNMILGIc) zR{Vft(XeaBe&(B?z|@Kex`UEfP{j`y#5b8n6X zk|)KUSnrg?)6~_@&t?pJd#bd zL+6hP`YpvhizV&L=ShCFrB?LwTiw6wUsxT1T7=z(Q659v0o3&#hwAU# z8*wElTdf4C;GnpAG3GT3Fr1C_XLGY{=~|l4e;(bJ46x?>KdRb)zuy0PoB#Q}l#(7G zBJSSmnYS?&#dA4Nkpr#)@Yy#-eX@ln=^Nb{z6g+PJjLt1<62YFy2YTc&iJW0cj7a2lkk2xaDW9uK1pS`mprS2IL_8CTwBNKIT7Y~t z=i+f?{HaY9%tTWqd${XL-8`^EDon%Rc%ozD1?;DlSBa{f7R7ax z73F@x%W8UUje?CNv*xR47N_OOR2~-)R&BHrZ<#%=3Undt8{yN%lYMAQu4eY9o{ zZq>S}tN_(&Mujg`*D;@%h&sr46!cKDaH2o~Rl&xKf8P;OL4wSwPO_j~wjaK#cEr=w7-8E2r+ahD^58#WO zw?D~2m!{fzv^x5kGgcE0t@TKC{jTF|<`J5qpS}JGMI=;5>CbpPqx7ogG_R>;ZVgb% zOXrpHRIFR|Y0S|C|06swgbp(KgQlPNS;6>RN1j7W0>TW7s|B&=8@ zY%r@VmY@T^rC^{A)#2$6`SO=P3N)9H^x@5UirE>BCplZp9$8jXx!hGJ zcUDb#{N;N}>m1Fs<@>z$RL93^uC6LHOv--zpnrWKMMlRvP5#vT?$!iK)WL<{zExFy z`K!v#)hqSW&jVz_AAg(_-w8C$R$eOTYaGyP%oaHa_i2gc>)zd1X60qcvg5S5Zi#o* zq%p9~E3a&uYKjBfr2M9C2E|0$rb109U-coIdOf9BG?XXq`r>-2B zPKRzFL$^~?e;5CzK8;z~?t$B|E^|i#YH`@L4eBsc8fXcoD;d{5Dn{I{>maY;e9rRg zS_FnjAf1`Z?Jj@ntmE2N)s=j(zh*9#g9mED;TMowzu1%MkkT+g&O{vAKWT!MOQ=nulX+ zbsJEV&SDd9mjs2v+cjcYIfr)vRPkjC0ngEKYGXq?>P|!K=tBREU^W&tt8u5DLumD2 zYMO35jA}lIOa`jS^pJ^BjjihJ7L>uR>%wNeN6e}BU}azn08_mo$3u~otCemVxTXaA zXa&G5DtK;Wumi0(G?b@PF*i#b7hbm1d)KsSpk}32EB&{zRd*dY!&Xbf$jbU_`si-D zsBhWDd|@GU)$`Z3ThpIbug@8oqudfzcAovt94jzX%$#mzmsJkA91tZx=v}Wfl*Rc*IEQ}CcBwcpwxa7 z%RmXlJz57!knh7n&mSTb5Q~)i7Z}bTv-cc6T*UtcSCyks{+$n;0tc zVcpGGksUa9Gg#DEAu4*8?P|U5WhJl;^skJAch|uxP47t$s~b`WU94tU_o9zgPOzqn zqlU|-we8mQ*L3T&**#S&MYA&GyD6LHnRHS(9U1ekO6OWocM|C~R6bWU?=YM?6|wMz zQ>_+gpSD*6ZHvN{vVFD+W{oD8jcK(im{qIQ1&eRnUV^S^)TG;2nypm_Gw#&fc7u!2 z3~_C*%U3p7*UJW1+VxF4-D_JAoo|5GHeA=^y4%@Oy}K!48MI9x6igo$6MKT z&DP?y?fvY^2HjM#e9(8$#cF_bRmL1Xx8WUa3BX1dwIvw$d0SfoeJ@wGjW9cDWI34b zCfW5(*Xm>$jLtX4w&9(xjn@O(`0hC4TpL{==L5UrEpo>1@9`=*gL$v_$u&^BUnj=Ebeerib5gGoyH(1} zeT^z?i0?vh72?A4GQ>;~u4=l45!GtD)fz1|-Zl*z$XnmEz2>{Rea#+Ox84c>yDO(F znzvC<+jxc+y0{M)(FTB>Wa09>@5)Wu!P|5LC-Zs3zD?%y&9-YYpBKzl^Ld-iDVsHa z`jEYIK7GjeqJ7!cD%D)SiSy3q_i`?*0<+nX;7+B3k*uy+2M(db%6&(<$T6BfE7p3$ zO`tlbmV8z>mE<-!M-0ugIb;uV2$ec3P-9QL641g^2Xbxg4cL+db}tU=1vo93P-UBc zVS+eT!e$o?*rrx-x4YT*OZ89`(Hn`SD8f;uw7L&9Qdd!W2Ijr| z51mY2N~GB;?z2K)b(I^Im5n;y+0L=H;pz^%Fu2uCTK3b*1FNY~ds|0B1gjbb3|#rL z#vV{Uh3fRx>QPfl$nFeZb>qCup$Ia^-(Ycrar`EUC<=MCVYN=NJx#?B2Q+SKo4?Q7 z)u^sDe8z>;mfsW|qbn?@9^@+4lLIUt5+-8#iWn?;G628MG-^MSb(dQ9U2MY=d%Fz> z_1;P){u4jC&(Pnhtcm}5aPW0G{`cd9N89{Q@1?x$oTK9-PSN3cUiYAVYja_!STRSQ zWN=s59MQ!9S6|#Y!qpdT0*ashJiI(TJ9|DHUVi`lUzabQnemrU)Rc&$7|Txr8$LgK z{rtj#*#cmW@)m*r{_NGiEr$R5GaSuz=&k$jV-5e`e|%Wx|A)u>+x-9UrR=~t5(2YW zrPJ0c0B&YDhH*+Fm1rhG!Ik9ves2d<4l0vhhy$PDD1vE3$1q1Cn2~t82Mk9@kZY_i z;+*Fw4tqNg*uG>UOo5c;k)Q#<^DfBJ~%!&Ebae;uaCC!-+L)LS@L@w-4kqh&)zTfFJSllpJxw# z-L;ADZ*Fc1K|@0t*kt(Ltzak|dnVA};N|JX^Wnun$q)WaNF|hTKwzaEn_Wa@&ZJSQ z_{4-Vojc#x%txn73~>_C1#cKU(72FeoW_FDDAG9>ZId5uK8(DFUhfUZlQfb{=Zg0p z!Y^5+Z;6Wcm72Vncmo1F8`q;7+J25w&W1x;B^PIn)M7=uc_7)6@t zhgcvIarkA18E%C^ITp_hL@m!AE70VT#>ACitRjU%b>ZRMXa*4ZgeRyFS1vahiS;_^SgZ1%J$EuohHf2>` zFfyHDrW1_Hwi5ev0s&)r*m6nB!FsuN!R93{8GsF({kpUET=h=j<2mZlh z{{TEMAK847|B%D#^)9m61}KIxhCoq@{EkC2M~Q$5jUsv@e`U9+;|M#6{7!nkC7j!z z(6%h$8Rh{a%Au=utIkDsqx1W&uGU+E*R{X~Z8j(^yHRr(WkH%ptr2A7H=FF(*|t<$ zq_}xuj3XLPmE_%(4kNuNSC320p%TJ*-CUL>UJ_FQ4A1GcyL??2?9Iv&M!)>J91L!ZVb%;y&7n5h z_^O6Wc&?fl7gAuDliwxo@3(ZBgA?!67j2f)5>DlcQCYj0+N_p$%LCa)(UxZ%O$A)M z91e!Vmk?kk$V44!?UvZ4b4YH3A^Ckxv-Wd<2y+f%hGIBs7E*LV?aIV9o&%$|i*>Wu zXby*Pu3-Q7ODP|IcdC9VnWnj$&E}xZ2rnjY;u%Jfm@Vo6WOK0MGqUCfe2rru`p~d* z;R1=2OC?G2uMxv2tc7}~IW)!`Zfb1zG?y3+3DY`!zK^nm(c3@nI0t?CTmz(e4(7i4eoB`)6hm0I*ltUBji;%)Jynb61}$Jl zZ`8qUIi_MbN)j1k6F)lyG*O!z3!yndjBm_3Y?#Avj+l6(S~rf%%%Mf#>rnxC77knP z0ThT7MQR2{o|PIRy(VD?ouqyaR!>uTVTGKTzh(xd=HR5#DVR)fga!7bvYgP6 zdnEQK3o(-j8e#!rMz}oB7Bqz$6bm^C-A0i~wgD1@Y6CRR&OBA84VPWQFTb{#YKu7- zr$H~d4n`LEnmJ@&cjR9M85x&%n9V)TLDC@`e!DqjcGnMN4xC5jrMQG=>Kz9<{tpfW zHYRDA&3`M0h z1tT?}A_!eYn=|jbWD;cEfUZa!t{9N=ym_w!Qn?mJjRVrD7~bW8w57%5W@xiLmjy9R zAU4GU=6F8F40aP5K7ij+6bm92KAfTWE0HN`^-4J5c1?e(WeKCBwI)PMhDt8AhL%eh zCO9CIMfs>oxwWKAZ8OuyrZ|>=Dcd?`+@xcJU86wL7z0-T2@WBom@CTN5-uLR3!MB3 zWeKCTYb+x}*%7~4wO6|#)5f$g?KtpnOQ}c%ue|gfRmdRFbe&D3mJ9;jx7qa5DNC)S z9UXgzPnvE;ie$(?s%Tb!;9`a$(O+JqTniaS`BE_M%RKSPluk^^O>ore4q=i;Q6|F& z4Dp#todnEgN!k`&mMvtEj0`$7Q;jN60cyyQTYYuFClXI#RDV-2Qh{E$RvYpg{uk#` zXlI!7PkIY&LIx?bud#ATrsQ~T7^@y!NNHbjDXAJOa4EZg`Tyf9OBlVRDEtORC=S$t zTvMmK8*X*p*9(Rw6B1Mp3M;S@xw>aq%rMI+A@SdEbH-;hjY9Z^(Nt)SR3)z!x*K(; zWqieMNT{}&plT0!5e-lT*jQL1d84+P>@e4sI^B4R#WSm8mqIdSZsXAR;&xp0-nXBzXY4M7z0=coL;5Hjk9LB^1z5Bu~JT2@Zsb zWBH)fT}YV>HvR6qB}0}&_@2oS&^W+}-~(fK=3b1&4P{p(p4w-g>C@9<#$YqtcH0e| z$q+f)M`vq!jp8ZJ4KF1oVO_vUz2QznPI>R-R8zkFnG>P%u+x%dYv5lw+|c zn=q^|LiNp%Hk5t5%3K@IkyT`PabA0yCdt4hasqg9?iuW7*P2D7Z_8%8VXXnUazwkP z-RyA2VRj$&*_Sr=vz6Vj&Ks;k2IKFwr9e~ZM24Egm)1gF0;;~pH~x@LD+qKmMUa3| z&OJ^Xi+qNsU73D{7vZlbU z^1yijB>oHu)W!j6B^iwO%S*zA3OlJTDc~Q}y5-j*)S}*yXis0@Ug3oml8mp6f+3-* zYbVMaY0K_4Lsm=dkF+ddbg=LL^|62W_`pBduQl$qCPPBmJ0OEWv;+=us_u?Hxw3@O z(b4{KJ7dN=GTdM?or%%AAw%W~sycap7l_Up^chQy%wgu=Q$d({ubsO{v^7u{5vQ3@r zY$&BgXl6>T-r%TluQdQe6lLYp^!nVlM<+6@Ri87UwM^;m-%fq9r4v&sWSQw=%oy5+ z_dfnlq;z9S<%DBv&xTM|RNN zumrclU{?l1Y(_TgfP+0}HrKxN}hH5s&Q5mQtgQdXslwMyp zIUC9eC~G1Y)RJL)N;j5HWNzqcsihSf{6N_N(Uv7iuLzThRXL55a%SG zDz)M>EuF~Fm`1e1lrCl%RB$WcHu0%-+55#Y`JK}cE(}o6P{9G)(ic_R4M{xp@HGjf zCO)tu^|%7+1v${chv;J2?e@*2T=h{b$f@~Z(fB>i+=&ZE^gM|$ETFQsnyh76*Pn=W`T0lv%1+@hA@SbJ^z9 z&6DBID7|WT24~@}oIkw`kS77jgLlOTEFb))>*QEplMuL;5Ork8G6-)8QBykYhMKJK z{v6%DiP1G85gOaTRZA86nWcd!eB>~usn9`=5s7f9Lp&kFzHAhY_cZShtSn))CRDFW zhH4`HdKYkbZS5Aw4dG-mHNo!4=DHsNWiWCRx{n^{53;0gS2)N+Bv42`QKy1VALP1Z z$PrlwbcjWcIF8rHS%7`QfJ(O{SA)Mj?T}yMik~ z6&WPj-D)~2Q<(;E{?plKPlqLpyp5QW{;>EJ4Que>kfDYp>6SL6MujS$Wm&@LHAdmj zj0pT=9MrC{S~5tEqg~#s-(6{&QfwS3&wo18kpYlRw*toGDk%J7a!bgQyFtjYS4QIn z1QAtni%)1ENI+xmMI^q;za&36K51!((o15X0}gu0wa$8{$2EB1zdr(4jJ0qHfHib5 z0omLZ7bPR!v#hp`&(;xgpfE=tSGh`dOr9{=$T#+jJ!}Ji$!%JW%O9Jd}{Z4oZlp!14z+dYR;MzsMaZDQ~|~d3RL5 zC;DzohE;+0Rv!+s<@QV{mL-gyd`sH8?O`Tf>4=MKj7lbwkNTKO`|B0s?#(dJEO807 z_oKy}21#(5|iO$Hr+S=J;W zW;nDJCuwB%&4e@M z(Va^t^)2J-LmZ?kYy*u2zO8%*r*ci`1ObJhxVjOTR@$!QggDXLLK($L%mqfFjWiIN z47c=Cbt3B)G((*Ps|a9$b|Aw!4L@fB=+c%9iWD=X5>Bm;(w#$p!*Q1H5^=aVJJ(68 zvitszJTBP%mDX+nmI`f?%SegIP{3rCNocrDC{tfrYYvtQEz*2i$d(G*%qHuKlh4H9 z`Q*@l;up^CDivBJ)7WOy=uleUzE!BOCemB&Uf70vt1qoRx-Avj#?2@P&afn@4V__M zey+hYI`4(Hao(K0unqRMuC(^qvs7ps=B-Pdw>Ih9Kk3q$3OdgDIU{q#R%#Ecv-=pt zf8ObQveH>!C{kfXil1$ApZ2A7TAzl!(466CT~Zm#h7t|xb582h{yU#~Dy&HGb2ln{ zRylp{N`)2qeYTl>+LzAyLNgUg>3z0|ecG2h?u8Y>y0>|K+LzXs3=MmsIXdw+txx;1 zAr(y41-?RIu=)ab6b7Eb@k|Z}KC(x)4;M3%%d_^5XF}NxVk%Uh`IJ#m?f+f>B?^hT zkW7y60g6K)0vL)YHjVy zUG;@#wQZ}lwJ)8i&^EzX>Be)LWo%XHOog@?l~z$3GzZ6co)rL!HWZ3>?TDs1!k)Rj%Cur`BF0~NMu zeCo=kR9Kt9$DzVDi_f~U>AA2rf6po^Y!B#C=}d*T>3hl?X`91mMd?h1w%L2O>3ceq z&gVkgDV{+&yI~Y!mpbC>wC3cBy+>=I;6IKc-TpGe>Hhxo4ZY zr$cF-rX;s)w@cjfCuZ*X`1V(4AB?tnd;ZkKJs%q#I`4(HX?wPLdpeZXA;T&+p4*s` z_g7j+3~QpoHl*Y|l+O3gZDT%fBT9BCt;0PxQDGZU@*c{a&xLI~$$Kapd%~TibZg+tay2>FkfxHr(^}bnZ}EM|y6cLgV4QJ)Jw0&Zl$RV9(prxkKr!wY81) zybbo;p>)1qXdCKz8|%44=@jKz(SiRKwF>jBBRnhH=J3B?ZbLjbm-gA)EdJXBcwXkX z-v)PHS=#4>vG{Kj+S$Q>Ijr-x6p9nm*<$$ci|C*rA+j!1xOZ!}^75uLV z=Ult~x1pSCO6xoR4F7EcIhSHMXT+!t;+(zx9HTgQU;k}FI6Lcq8@_pEY5nzPhW|Ff zn>)m9b_w+vo(kRZuOlL?Rs&W?Y_=%hhHUN-f31xF6_J{^QNP-k)-k^->%Tch^ZH0r z8Pm}lU>oggHI3@ zalP7>_6Z-$>wi^5qisa5wxv7%+XVC~>A%}xUX7(Y{@aA|T7~~@7%#VU$A6n3UfcLx zZOaDspBCY}O6z|cx~s9Y_EyaKPm91^jriXN?JAYl9&*n8`}&Yw+h|>FOLzN!n_ykb zl395h2c)UAj?z_w|K>1VjriY204bFXQPs+5iKzcL(`ga2(Pu9anD{=YI1*DCyPk6^cS$A6m;TxI^V4Zqb?y5ql1 z@U3m!t+u5*|8Em^t9t%!LvGcV*73G#@ZTJ8tBn6`u&t)D3IACiYO7t8t_qis_-xI?N$M)SJWdP&VM@l90{<5k+(Vj(azkjXjnplgZ~=tqg&a}BkRfW zxs)Z0USkyg%!t50#zFnpM9u5J;alw0_?N9dbF^n=38N?9k}mR3n}8hKSR8Fjcl$$| zP#oJR9BoUd7#tCu1~k&<&1C{-aUTylTIj zFVE{<>SIh}|b#Bgp>yt%pY4LA1#Iv<2IaLkMu zNB1U_dC6@sm}jv4nV3hLUX->8>I6AD6fgAibIe`2=dWH>LI~Z{iTB=$d$i8y=ELC{ zn^_Sb0!?e`)Zs&*>3f^YeUv4P4*f@m{%W&tad|Wd4n=f)oDp`~NhnoxykB|ay_I$X zK!y&E*)?Y0QC`_mdUfdcz;L93YbcYP`SQ`;^uMc_+)G))DCf$a8G4m56wx8&vno2| z)7%27j9~H7MO7putCL{ zA7e-mH(XX<@y_R-TAYBw5atX1Ta=4pAq{vz7PDHmv%66odh<-48yGJ|FHHtoW*8`# zN;KO+VQ9I*0|=bk=23DRjI=ei&E>X>4rAh~;^ZFCVS(Q>Ml!RUmRF02Fz*aDx}xJx zQas*UX}?(%O2#O-N|RRMZ(DY+RUHl*GXKCL<90Fhxd>AJ1ab>S5svPHCgVf}0$s^t zdY(ptBoWTgMPjuTWjA-|;Vi5MzHb}ouexZ z$ZWQInCph)Tc5cit6N?{Yhu*496)6SeNakv7wdj4f>s1kZaIJtTj%a#PInQM5V7it zt;+z6MN>tHlZTda31&E;*Z2dl;I*?=n!05K8IOh)kx<$UU}*dzGTr?A#l`DQ!QFdV z!iXPvDfezL7v7k}5uG;1ohl*-60KPF= zmvv$*+2)f3MOcoa}hh4E$ z7ZO|nGf{}GDJXN2)b|z_FNY9dCUm`bh|x{8`3izmjE&q&ROp}^XAvTE$6vS3@RjX1Y91RSC}ZZez5y0NG|U(@2d*PMn^p8DmV08-EfJK71GfFB zh#>pzgSD0^r{h1jNI7VouWwd&6_al91%AT-KJ3r(H`V6L2+9Q6A9cPLAII127#lpV zjT9%Sy2CIx8^$JdKmMeh$~!|4VpHzl3WjupRUh`|7eR?Y-1BPfVyy3$~@5<#l+oW z<^NiJ`&UKK6~?O(yJi3eK}6uWI^89_jltb}X_KTmtJ|=G?qr$H5M;8aTBYZ6_T(%)J1J*6^>Z!? zc^~VX^H~&T-Y{q5P&Be|KF&FtS(HiLFlXap5X^9JHTr1h?9RfOc*C5P!&l}W`mxSA zpGBLsoy_OjR5*fO_7EJKfAj;Ns=bhaaeb7~@cRltQ2!_$RQS z=~oOxs=WIH%!F~FWj+k;Z4=zpGxJ+ogn*3a3UYL=+8DFm47&jqEWB6vjfj> z(L9MTmp^vk8BQX)00d{oP5Oq28BGNsc%=iWNcmw8VruU`)Zb1VmXPo(-s@e=2nWJp z$}qCd>LI?)+^-P_A|Rxg$6pEMDM1)6#`BVp6nC(-##H4RRbQ+k?eo8v55w%-UT-uS zjS-*qz5sW&s(0`_xej=O0u0G*06zS?C-JK>=%j&BzG+XD^OkujIZEC0H0Ef6{}G|7 z;yON8--VLA|4prQnTb1(XnuI#<`59)ye7ehey?+qKg=E`` znF9io6}7;;EtYU-(3>QTCX66~;RWX@)^V*NO?)_$@-oCf8h52K(5Y^f|w!U_j=zfvMAaki9T7?Oz5e;NWQ`H z6yjtWqe#09VLbrVi%uacKquWbEP(GhUzCs-M9N}_u%3pM9pEaaH?q080W4r*&blrwLK`8!w8-V%G(kFPieKYZgSpXqf6Szf*=nY*&cffC`|OeSD`8V@&HDPz+da zeHVyg8eE;niWW9mtQ^vUro9^+MUqD@Xlf$ANmQkAtiBj`A=!R?cp-EySpkzYiWb!| zBi_>~XOSc+ony-na_hZZzJl6SuDhoV8u-?3t>m zuBqvM=IJ4gLcoUjUjoxXFqp`wv6{&!aw`D%xJ)^;Sk2YBZ1mLmxRtfExD~bSZA~1_ z0UD}KLNXTi4iHz}Hr|Ka9kiapm)gJRee=|n^lF&oTW_DH-R!kH9BuD)Y9{#9a~1<+ zWd_&~SRt(SWuHG@05Xx7&GDX)Vhu+F?*#%D0c-a+@?Ln)l5$ilV#0j*W))~Zcul}3 zSh>Q!Z%>=Gz065`Fv zOr2oT-+Kd4eLQ@P_-iI)BonD7)(b&W$BafQ3}YoTL7YBULc*56m`YfTE;SRYy+v0+ zbKDA1Cc=gW?k5gXe{`_0!NAogEn02al ztBlPRjF2owU2M2Fp-TL*2+mdP5y5WAQ&@7klJ&w9I-jS_JBVj@eL6zainM;F0%v)t z89$gzNO$|Z3sHG=9ZOUe8IdNysJjO@$&{M{PgpI0G==lvn(4|gmG*yLZ-*Zo zxuh_|VTh^g&20tqp4VYMU+*9Kl1JeRsOogQ$95v>ASd4mZ#$)D%kyuy@4DA0jC)|h zDfYrHEC}c?|NH01Q+XixvTRof4d_pJ2g^?_p3N3BG01|x2K9308^AoOL0*Y21qOVa zxW$HGR&zxlFD3EtQezAz<`Xi~^_WVKGtJ@rw24hNm1xJcreKfYlvNAmQVr|QrJ^gs z5XQh?l|Iw0)kXo)_fxw5R4XXQb#)CcUHcV^cH1;`uPH)5e@28n_c8bwUH`k^nv+kg zHqe#%VRcgO+6DP4&qkA3yi zNAo{06#X@6)sIh(IeRkAohqVC1Ec)26rWGo=FKP`bLhYtPfhI6@!`nb?kx#rbP;^z}5D^UvBCVnwee>&RhPC^Rmn8`GbsqgwA zGbeZKk z`Y|3>kwzbJ4#^{vh+I~6DAio2;tA4??~mml{uMZh__KU$15V}Gg5;o+T{R)8=o3LR zz-nd12Yv-3!RcUcLH0@ZBDt3)i4-HS_Yx;s^}WHMhS`z_J32ytLR#`Q`r(}3g$S=# zI>Q|65A~$Bz?AW7?`-SCE}Az2Tc(PkJ%gBPCUL>3avsfaFJ>Zk+kIA<^6ngoSmH-2 zM)Tauy6fgCV9|)GlxX*_L`JcfD@7ZP#~Z@ueXUVkRQfjr$^_hok0@NmUb>L*c;iSC z5>&b}FSW1iNMIX{1h&7KDd!w})E}DP{RI+`3eiSMgafg2McPirJ>E-E6%!G|)FxEq z{CRuW+VLM7x%XLUI;rA*0dMLwtfwj93^^B`bKd|J*{0YzO4qQYvKxj zH#woVDVeZj?`5g zY-)?3giZ~an3>(2uWRMFTeF7pUAzn>4#QvNK_QBhrx9O&zHXX!7I!n#K0nZNaKkh( zQ}FXjN24^#&6xm#+drt4><*4|^bTpjk44uCp;;9BCBoqzxG} z7wybO%)1{!9xUe`*gqCgK0+gm|GEZ=bsRj~YcvAh752aXPvX!=>Msbw+@32&4@UwR zDXTrSQthT%E5y#FDgU>Bqn%0*_cUC=x3*;Kgzu>cWvm2GrPptKR>~l+r?osDx^(d1 z!e0C#)*oPjhVXI;@)I3ixMy&WujmjXof6I2gjpjvi5q+ZgH8lffT8geL0%oR8JMOeE5la(Yg(Tv> z!Y;sQ&(;;Kt_E3?D^I(~X?5-$puvob^2elqRd^Fx-;6OV`Hu_Fi$=JXTf07PD@I6t z#S*b2F`mwNZY#@6Q}~yurxy>Qe!4^R~pr%`RpVBLk(>iDHq3r69oh!bhtYY z|(N9}e;0^N=^yZ?* zAn6P*C&;MvBd$twlt`QD^IF(l6y#`e`KFjwyqh8r>)Awb;VVu^%J+XiE_$_z=Bjuh z@AtUn>*-J4XON1WYdpAZw|5R^z94;?Q=n$wUfQtuNjrKNLSM+ABO0o^ zK6aP?s;9W38=p20!YbmQx5upn^+(&s@V9yeKPMMAZgE-^!&5ockb8^OtT7PXgPE0Q z?0Tn?eV$GB^c#ktxz{66sr&B*W@zxl&eD=GxKuN#qAt5J80E*Z29^L#te}hXKZzq6 z%pbujiT=*9&p-sBdD#xm_K^2P=zX3`0Wb*oaC8gy@%}imclPsfZQ-0)5YuK_jVj8| zTtA!7tfANP{KWJBV8e^nrrR4Jx(If;7M;AzV*!3_+thElC3(_Gx;8yQ(>LSenYX=7 zKD9o4r%yH_%Wf9$6%gsL}Y(t}uvi4bv$5XjodF>`g9K&u~H-pZb zAig)>7a!1N^K0Zh;Xs7ishUB;Ff#j4vPGgnEVZgx{{iKb>Hcf|%6Xgw%Wh?p&*EjB zZUoV)8Go<_Ck==O{Q1xjdx5)!s^?*PV=>o-!hl_i_{!v0ni_o;qt5oNyAI6oyF&l? zbQ;(G`B!Bp`_6=QNLt=8V)B4dbrT-?z`N!ckZ197QfkI|&Ysf2~;K=z%W=_jz2xWg@<-#{PyT2rp(#;P35@;3df6DjEfFmE$A5^Wde|dsu^Ll{p=b!$sSC@O>UL2i+)Rhhn z9^vhNvCEjMh{@NkDZOdWkM^oI_>n4uJx-NNG1doh(0AM`Ba5OGV}`0-pV7$neTBG? z;$A$2tGhRr5}|wDGKZ|1MC2HfPR8dxDcRmW>rT6?#wsvP0`tK1iV@|hp|g-(*z+mS zrIP1d5ICWE$yAkm@@fylw)a3Pdy=MWXgZdIqj~!si2HWSggPe*?)7`|$pC{-vhV`F zoM2nzB?!x{$g(UO=s2X1GIa~?#9%Ipo|yR!qEn4jqGs$xN){|D8&#)!1yu4Z}P9pn$T9i|-HOZ3(^)-b%3kVxf`ht~hp;kw(ll3aQv*6;PH;p)p94TM9SH)VM|>tiQ#n$(7bq zu(vV!-CF9(owACM@g>k}XP(a197AWMsLwck-#eU=`N9BvBF)N?so>Q0Mlr24*D4-p zn>d)~cBjS+p)`Acr@Om4ITdq!$YReMep^**(aTnF7y_}Pa2RYuQMjFx(_fmM`rl~# z8@DoOR8$XKFE%l$&P;=L%0T{7foy8LC2$ocA|?S~5hASAISjl+Akkp)f4T-WeBEA_ z8TTT5fVuaO|HL-aB;o;}CmN+&%?9Q*rF8dIa}Ul4;=(g6pj37B(t+^=SR-@c5mgH* zi4XI62~^+gyu^`y`(NLtk9M=5|0sfLE*h<%68DlmSYZ6{CnhTN$X1Ns8%_fyy9yT~ zWQkl6=c0b`je+`i@T>{K@Al3An$m0DI!in!9r#b4zoORqr71^(|9!EN2#tfw-pV@m zl~0IMi0m-li@l6VDmR;6kN0tUZhQgdN&hv$tM5?0eTk6@1=?j+l^*GG_q$XJ-QomT zAI0`RhW%eroiwOS8|yExx>z>QYQX}jR4U*Jv5?C?Jn=hoHp zk7cu8kuxqMbK|!p z^XqZ2tb50MCR|NY4uyVFhAA62aR7%he1FrxafniO;!I>>{$FuNpR3%rXQtF*yi+x@ z?un-XIL*dFq-8>cSXSw{SopK$J&d<~&pX?@)1BE`X7Ja|C-oZ$cyIu`KCWtn^zsN$ z+UXYZbpu^&Z$f&#ViNa$DnBh&wU+ry1>+)GjTzEEwj)Pa_qb6BU!frAe(##dWuF$q zCJzPC4pIh69fZmlk@-@TK{+He7Hh)@C~doGrT+HZZRP#T@E;70YCK%zu?myD@`!f- z0AGO~Z?*2gjki6B|3ku(@-;I?w}+;QGjb_WBzfS`@qU)LswGrDwSQLb;gU=7RoCN} zhm(!{$5<0)t{%KAhyt`&lfycP4Aqt(hR8FU{)Ry9Y-%dqq-yhAU2F~-$EYcx)RGKKQ4Od-bdcsL8IGlioxjP>eJ>6-w5Fuxc7t$7x$1O##omg_@aX{FH>ao zj*g&^8s$q}j*V9L8{*b<0m|;E3&PDy-+Ng8v|{5qHc7XcofYVfJYr$+q;@yU& z7O!rWxhi&2nT*hX+RpD5KCT#9B(sarT~(+46$lfoNw65UZCsjut=YeruU_#Q1F8&| zB>!07s!d@gPf?aw`SjJE8$Ml{{_-t-KxK`TH7FOh1>Daa>N2xqk`g_8^?too5#V8r z3=M%**2|B#y~uxEfN z^Gy8^mM6-_p^Fr366SV)kBDg!YRx{dYf_(fO$cHG%U9onXR7=QpYITJW|DTfo6ceK zEYE57^PYqY`~=9%qH6Y7MTKvTqdfDC^;-@iVNHvQ z9@xJkPabne*_dkcU3P5P4%uHSkbT5@NgDM9Rj0jMA7q7ZX`F72Q67yR4sO|RMZRG3 z9_@D~+5Yr0dvvni5}o1-80x8KTm9FL>*by5Br)kcdE|QhVj471-@f)*I{ln-+&J#C z3xK;61_v9dm;6>uj7rb{NKB64D%-_CdvxEp9X?x6K^11YahpMUo~%Z5*#@*McIDi> zPN~&@Z7QFcZv09eZP?qKcuKojycWFm3ZCA0Ho`9^ORsjJ-~Wv9y_*Sd$!$?%r+eK zkOu}w%+p!{bTBecp@j>Kjk|q8GnS)K0G82*e|xu!4UlTZ&e(~vOJhXUKxd6 zH!KWlY|_o;Y7K)fyDkel?apUp$zcjLhMtR8nqfY+`yxcnQA$B|c8KM2h|jBj#uza* z<3~=CWKW|avtN&C#u%wZ)e4zRbG2@|Z>vVP={6Jq6BY9$8_2)~8I|JRTFmpRgAZHk zZx8+}rEEAk>U56Q2Khmk3ww3t2<7HLNq}#-Z3x@S#8XM}*D&6*M0JW()oN$hK1(+7)_# zS(p1-tu+=z{@T?K82&I$-m)1$t}>op^tu=^RLPc85}=k8gP;cnQohIqTT->92C`AS zQw~J+-3-(hJDX%#sdoXqfLB?AWs^UzE(N2|(u8uW>K;|`*BUO+4Ovx=|4fu9+?z(= zmg(Gw6}}d#&MZ#aGKQ+%Ml4)Q>*FO!N&)5{hD*JfaeCmvC#(r+{K1BQ zk0fIBm~<7x^CKm_d6lRIan3||(jn^r<~g927%AN&H`k+ta zBUXBvt$5=HM3g_n?+`ksNvQ7(hu@mwJvlYE@Q6IT@Ni&!r^`dofCcm-0D(MRzPl!d zy}o0}6aPhZk;M^6?M;Qo?&^knDu%tW5kM6|1+am$4jDR3A}#?yW6~KcAnhZx>3;=}(-ZVp58l{ds)T2DdebByf2?qtTbCY(5CM zoccPXbmRnWvcne=_qc3oTZFNgTq0_2y_r!w+5%GyZ1dMMZik-cJq!&qBqrrqE*}e~ zAsc8kdYZ_IyZg_T%@^Wjrb{-7YQ~4Q5ZX?V6h>A0IrXzJeD+YcBk{bJ7kotgu_oHQ>yx3BAaGdjZ!-)ytl!nM zF`A#VoZ9XasMz*1O-tqQ=*o-`;?#e(`>_y`MD4$y=i(Lih{H3b{VGk>O)74uZ8fA< zdE?qfwVso?Q+sW-?1>S}K`djhekvI0Te*_fwW>!339}Rq{+e^jt2vW8bGUHOt}% zub26vwDgM9-Q7n!X2-1XT$d9`=)Py~mwzsCr2dQ^4{4*M?~CBKhPTE3SH9Qvm-qfO zWuzWAcd}let68*DRx1;`USeJnO~Q(++I|STEJfq%jopc??w2xHNzj2ZsCxBdPA)6! z+*PsHNz!|A0E653h*0a?$M+COVUgBDi+O@=WNnAxT1LKvtnccdBDor?;l*R!O;sfI zbSrn9Vp{9DKgaI&)N-^FNn+5u)chda$AaW|ns>>T=dprakYym|YOsTn@Q1hEN67lk zHfO5wrCjSvrvZlRr(Z|Un{h)`iY<(~TG*G4;$hEW>I?nrN~pp$btyeh&Gw4010s9N zDtjcC?xuyqUP!O#)%9cC>(U;H!W`N%jDr~g^L6~nS@VbeTz(O!PSR=>@)RjW<3=Cr zwJr;5D%4t5LNmmRnbwcL{Oq))dK$MSnfR1Xj?-OLM^nl8AM7K;diPj#amaLM+2Vm9xaBgdyA! z@KmzT-BfV=CH&O_@mGUzEPqGznjUqj=0wz?5_M@A+eF2i>`{ulTm-_HV2NxDPbjcL zq6?)}NhnY>`JbX9)c5b#j82QC!jNtzZHJr{kJvfKI>4QMWr^#>GwIs5l)!W5ttD+&5{N?G}{2WEmqqJ ztyf>AP?bFRV^)NP&F&uBU%>Sc76*FA3Shg?FGTE$CCoxjFpG>#Vwz6zGBCSdW!a6M z2Xu9P4(il)V(~_Kbus+$QXoNc=xv%Pr-lttW%zIrr*W6(qI&FlOe5G1Sn=QS?|89} z%FJo>D?2nLNz*;3MM$K=;GvRAF3PeBo9z+uc@s&XCDTpN% zahXbeY5r&=r_Y;CKY5ko_9V|XchMM%rE3+15zK?2j%BFnROCe%QrtcV8h`$qi+3B_ zIYo#;sFk_u4ZpYbx0MF$AFuUh<9R>18U?3e8O_XeW!Ju*9s3X)DS7e7knSSV6(i}Ss|>WA2-plM zm;oOSITF#`&|L6X64-CpPlqonYYoP_+-I09*;H`_DfzD>alb`l)tZhBU3=Hu#b;xm zYq3WV24Zby*JZKaeODtt4$3o&z^D9oK9?obt1Z}~MW6#TBG{%uG0F!qhwtBUDO!t5 zNHLY(4L_!mXA%o;H*Vn==UPSQAWId6TKig)mfV7U0bzqsUyw$1f5$N2<3aAL_s`_# z2u&QphgB=q48QXw>_j1aXvWaVR07x%@r4Z#=d#iJzEhj0L={ly|8g-9WrzD+ZH(9w z0EgSyj4W85&je8bMOZEH4wr~N%8VbYNs*oN7+#yN4n?%*YmfG;oQ&wwh?s%OaY0=) zzZ4M^aHcN?d{0IsB2*uJWNfz;Jns@nIERdYxm`*zL9R@XK)GjI4Q?tf;pFh9_8h-m z>ru;!CF8Mtg!K7bkIW>Tvbvb~YR0mWJVJ>XbHR>?_K;oDpIQupfNoFn)Culsvb+pr zH*a_L|8_Q8I@`d6k4Y!%lX>jTO{<*wFkgj%*C>*=oj@C8?)p@dY9p8!z; zl{M|+eNLy>rKaC))PFd+skkbGbpQ3B+H0UvuWh&Z#jwEmsW1~Qtvxtj)u@dkYg{LZ zylkiD9wm0^NVsI1+FCU>7jB`)(9V{XRA_8<_A$*7n0QX1@mJHFd#rP{)s?)5i^6E9Mu4_7mC&Hrb z2tLLFN%6l0Za)jtpkLMW-w^y?@iDeR4oq10!TT7468SBHh47)UX9XpK1oEPa5}Q%w4bQ`^Q)&`C7nF^sByp)Gdg9)GlfFy*;L8+(1%YEEtgncZ3gU1Sl{% zFbodiW02$?tv}A*ZEsK7GSC5-5Ri#)$E}p5O9`_F7@z@p)9621k zJ#g3(0_GxMzgi^hxOI+hNOxRcg`Ep zc;Fa(%j9N1>>kIGoc?0k+9B^W; zs0lo@h&V^lY}pW0aoCistNPRVr4J7~I-}KZ7_7wQf>+SdAn*pPI=}wf)DDTp2WIxI zPW@s55>%P&dqb30Iq~r;a5kxXeUnL+t^DyL(9U(Wk{pUb?NFALvJ zP9{8c5g=DjDuGb&&ABu+ClDi)LX|3!lNAPb3r*Zv9>D0h3}>`Ls|qx?oFXcwdhkQ4 z5*Ou?(;8G@QInYBRx)QQn`XU)E4^8)nT<9Qd9_=pP4nK|)-9#j3BJRXsN&y3qaoh; zvC2dIVIB<%&qkJ_EUHAAVS|t5c5VtNLLvI+Q1%)WrkPqAO%-f6g;Z^^Wtg8i0*A3S z3_sY+N6g7*TS(#XvWcoBX^n*&^d@gOFrTg%hGPSf^In9(baI zKi|#!D)mjB!)WuzQR};uViWE&74Nk%^nu9FM#lkrL?!)RbIyVdUf9-h>ipgKf3@)? z{&T4BFm6xKP`isCA?P@^Sxd;I66f?$H9~N}EA6v*D#|+l2-4#! z`bLBdUAnn^Jywe807r;as2QdB{dkGLOIoWdq-S@p<=lwM2>AeVw;^$ zi+&jU{NGhmM7WS4gI z^v>b9M6}Us#;rURt1}s*VD?*Rr@83bN?!b1Il|gXKHZ6?sFz=@G#70bE98kVJ@v65 z(-fcTd{HAhIul641X^Sj)-}T`PMf#GXPqmcg;w+ysuZxA&l7gSJ=G0wo6H*OW$l7e zNh#OUuj?!df)5#_l$eEoNgWOp99y|^;U08Uq&;^$HDsx=xx1&C?rZkT1np_V{S4$%aaJG4jKguCy4a@~IE(2lgQEE|(}p zLl+$wQI1rIMRRB`mGCQs{WN44R!&9+vo{J!-Zc0{4ikp&G5ckseXw5A*Z9wUX@;HsbXRn`pDTx$h_H3<{IoN* z(E@0}#5PBrpt98JQhVQL$RkQt+7}1S<`DKtSsKu31P@6LA%cofK>UJ6zJ=`3eQZr$ zC4oCRMHD}|?FmG6PS+U9t6%Zz0e)O@j~nQR7Pu3BbE~PiQQ+=a()6gQ-bv7MtE$-n zIKLuY=Gazy#QA9JdDJ%FXgPF?ce_~)#PK_Hj9$Cfbl#Lbc$IZ-Mw~Z%O-U;< zzN>8U`QY~7Ay8|C2(kA~;fvgn!y)*DQ^N=s!v&z(g!P3j32(Cqn@fp7`5JPxDc(dJ z^XW4!3xm)&8HOI37;n~hHv90Jf95*{lKVh%msb2%%Y&r^vcqJhK?Y7wo4d@(+TWb`-61{tTHra$*Zio=e|v;u3*&3VaFB zm==i_;}LtATU=-GkX-ctFC*Bj;3(Yx(y_{`7-vCo)Q&B_p*2S|Qpbj2qLoa|{T#N( z(r&%dP02w)J2~bC;KWe6$FXSg&HLpYVFo<|l>o!q5?uJhY6)B3q&&7m8yRXVw8`rq z6FefIxB`2gFyS6|6Y7^X)&Dz6qY3CEW=X^0SN+Y+$u2b3sh2x?#c_jH%A3Kn2MX=c zuT~~XtwYjvGe0{wS1{#ZxnKTWk)&;@=Ob8-zPfoCu19;(#jvxp!;w z=z$iXW4}mZAM5`dA^Z5mu}Qxgce@%4_$x23Ck$1%2iSha^4L5<#d*)rP&f;l)=&X+ zJ?woR(mD-jOxcCMtte4s9u+D_1`9CquM|!+ASga-W}p3{6%?tyEb$x81foXyh%j-2 z|A8iEC_;LKtX-0fpBaCQ;rn1>{rkQ>hL35VZl~&8+Nvab_xAZLwS1#Iw5y-s4%PM+ z7;!&X(tKmyeTS9Bk8vIx35F*n@$tM1g=cXE#OhBQng$Yv2=#D+v81u>@^-vB%O*@y&?cy;h{h z$+@?dR(?QZFt!Dx#Ezp|DUcRO$8`uPKs?m9k~Q}}b5nIHHuDvUV=5NXAg=i+RH%66 z*W1tE_65thT456IGb1F(U`|2eQq?8QOoTSehf)TQ=#`0ZkGtUXy>jE_Obb}n8-qT+ z94P=e_Wwmd2HRi!a;ReJ5_cak$YjAV|i8LVftHQt9p^lDu~Z{|NEp4un5pN%$9dq{_{T7+L&_~yzPiy z2Aa#J2up8JNSv&vvSrknyfDGXB%5`jTAT^97$LZeMpa2J2=6MGZ|UmOn}^slk7=KE-g@61Yl+r{OCT&ZyT!Q#TWB(ly znWhjL8gl>G0}iC3#5G)PO0u!;=$}l@H1(!Wq++2ek-R|Fpxrc-b_&v5!HLQYOXF>47?xSJ3Dt;eisiwmI#s#(HGH$5b zMQ)*+QEOSKZtOE`h482I=uQYG7~);zbw%h-ZCmyttmzXA$qB*EF}eXL9c>%p4lJyJ z$n$!~|;oSS+t)Ri%(#_9}WjgCQ^W9Co zpt500Kg)D&X)w83I$^BsW^0<+1>Uxmw*5k;A16jO_!tzHq*#JCK=&zH%$|%0)BR$% zQF8?6mWUo>R$bpt3GJo@SA>zw)gMCkB{gIjL&mDID&_*O8S92bJl)!v%;Q5Z?IMRl z!t%RLEk3g*L%obp<#-^a1{4_`1Cgm)H`6T7ln!!eCmRtRbT+9LV7j>U5!Rk3E1Ky) zq0fNHYdPPU;-xp0b)NK8o&Z9=1mYwL~<)<(a<JoWB76_|ETJ)AQtH`Gl}h;Av_sRX-BYxPG(Efq zUT;im3u$;>eEF)K3!arpxqfN*nd=z=jV%N|V}{{;aK$WEr8c>^R;g8PwDM=?0{e$I z%{ZOrs<5M6a1q-vWi9<#dZk_Ml>5&9dzALh+vA5InIHe&jE}MV+IDs9)Y{qSFayi$ zYYBM_>eJWTC`x5{0q*Y^&ROdsFQ1%w$YYoHrQ3+C?WHKh z)y;jcTdp4@h{DB1Jc=O`@_%zdPekO|K+7RUbgu32W7Ml1E4wYID+@$!0lhq=Eoxk? z<8KZN)iC!3mv~sP4H|^4bOG}SFuDU6)Ib;sJ^E*#1kAmbj)F}YRTfXV1wtpr2#WZ^fQihNSL{jv%Jg`+GoDB?BwA)OWQ3)4oZM zbHae2IK2LTfnJ>vzH}F7g5ebmf9@a1MsQcgyRcR%cldQak86Wzggqw-dj#sJvd8-H zVOb=Lgq8LOPK*Pq@n#5wuE=KMowjm@FlhVsREgmIO z(G70c(O}5Pr6X0}#nVYfPnstfUTMOn{``-=!H#XAzeCSOQf5*Q5c04{<12>x;Rll` zTf9i7vMdw!^GXNu*MH9!W}<%aNY$Xwo5C0iq%5R?;jMw;CWU}SlVTx1sJ&!9oHheP zPox~AE$CA#p*6&!@*vU3tq`@QNuNUsPfVo*!on8$e)XSiQu_pM?=YDYWl(reXNenS zP?y1+j_Qp4^vBAuGk6=+u<>C(9n_&T_na}Q|D;#xSLP!Lj+x`Exr$QvsUuAH*2z(8 z1Yq^MY#S(V+Gg062i{Q4-U~ZI6TYLb*{fqQDXp05XRG755n-4b-0%*;X`8tGU<}Mq zH|-$mbNM+PYLqDH{W4&(9)yU)V0PmF1-cfGk!tHMaR`Z(Tbpd|-$A4Fu!uh$^oYeO zr5L5YioDfyUfv}y>vP~AK*LRB{?#6Z|6(!6BfdE(9{~9sd{aDUZX8Tt7e347f-0~ z$#IUHyOfTWX-FP~in^c$S5rIY)$;$Hgsao_x9l;^2!Yr=0kH_%SXZC{pA z+AwmM`dj`xFyQNtk|E1aeyk*sDIuTkI&^?En&?pWzoJK_xDZ^Q*JaH6R1SqBX!jj` z^5=i?B+ZKgc`_EYjRD1KN4>3RtvvmmqT16j!M9Q+>_=*ra^U#;wd{xq3v7*}lU@q@ zU*{gj7b{D{uM_j9qubz#dFZO5sDw^Dt%twL=u~XQPj&wOd<>();z3s)sVuCM`yoVgL=Y9tq3XuNhCM+(t9hmvw>E9~YSHCa-N5MY>K=$db(^ zrMS1H!KmMTFL5j#*gA3^T7!v8pjJkgQv9Gew5Rv?$>cgU)VD{)W6#S@=43CNWn_;n z%sn6nnN607vP)n^uB;IQIBQ^AVRV|u-93F`W$RDo&Odd3Ok@U$d)gox1}flrJ8QXJ z$@bYGk_;-z*C{t}g?ab4Ytglx)Irm(#$5aL**6^<$b(~MV-UYBJn$ndJbVd0k})|c z>uE{s%!L#E&ok)3UWK6@<)fmXF_i;KE&isExKX2b@l%1uM0AX$$reXp^L=kVCc0Kx z2z@^2EU0U-?qax4>$NzrUC8yo@Vb_cPquhH+ol)~r@v-9k;IB^PgsZYIIUmOIG$+K zl+LyF*C$MQyzsI+PQha#-M#rlBmpqm$(m_&o3(KukyNW(!u(TXviY-z& zIBJ%7X`20=$0@FuO!MWsKiJ3u81`NC*xj&v5#7+od7sA@C&0<{K3pJ161*{K&QsZ| zhuBTQKP$9s#JT)uPK+^c$?F4NFhV9((fJ#T%$ z^krYMi$X**gk=Za?+WKZ-)?anrTIF&~e(jSISv|XCCG8}461ojDd=4tE^_qC!664`hE z_44Y&FoZ4kfv*`aeh&CM3 zW%2)z9p_ufMi-20YewWD1hpA4iFvb2)hW*k|0f3SFAHy6vx2_?`ltd~v+uVsqeE9< z-GF10&SA&(l~DIs6{rgrfjDzgL<`NQ_BzO8`FfvV)aJJqgNI!j(V;hUHItE193496 zttB3%2|^Yx44jzpkqglJ)~BBfP0xIzY4BfH$#qi(3q0LpOl=GAge!ON4v1aCnxa2s zg_lCb;gHnW&bv;yTbwE8`62f%@HHLxNqw3@Cq!+wK&yZ%R0FQ+M|cG>Xl=uvdzC1U z2nKE_B&>36w>)+wmpq(iPod6*OYPX&Mx00JEt@B5`gNPQTWQC`^+h*1v;shqZ)_GB zRB2|iOFFEFs17t&4pi#3qbQPWE#y}2K#NQX1a^$5ESO>x3i(10X_Y0GjXT%fm(s>$AqpAUgGau*%f*Sx0 zU9CHdvfzD0*3FGjq)kj&NuOGE6_13d2o6`+J@kgX&P{vh2C2Ho^fkE(GKv43`8hkOL|+Fm=9}?!yIHPfzkTRQ zI~FQrPZ5h6%l0fgSp)!FZ=B#%R_{ZimcQCO`ooC$x4*6+s%cDKc++4;IcXz*O$QZP z;{+C3ua@NH(;`JuK*LBTn5g~Ku@C)Uw=kdfjcC_T1zVHpRa})SqOce1_eYMlrgDl1 zWa`skqy%kD2{+4VgH?HDK@aQiMUcj=Is>F>LS+a*wiTaE;g&9dIXj|NEQ@>G;-`Ic zb5y+khEBK?lAs-)Ar}2dBIZVo)ePi+VXoq>vrI}vGP(}umDHNs`{qK#<3hmP6PDm| z=P9(e^a3LIXsMtVqXgO{4P_;m)uS1CKfqLan^4+QC_6ia75zZ<9;4!GRW=KiE{xKm zSq1`tc{sksilHvU6IDSX9p{8bXLl@h2`A=OzHV0FIV9o#pR37>Gl445$rK08iR;!# zMjv8(dot!F%3*Y4WQ_8=Y39EDe`y-UeJquno@ml4Qu1LK9?C$x!(JRg5Ppqvdhoi*qk(2Y|^UrQDb4|~=PsNz}= z{U*a*;<4!=5Fh-cZHCl~jgprGtRl%zzSQU?qMd%0rgY0C9AJzE`sS^EH{xDb`bR69 zEGy|W7Gm*Q7l^-}@~WQo0&q`*n!?zbAG)FuMqq^0jX_Rq!+H3RPN#DaqUhV-a{Aoa zB{Ztb*kyOom8;h_z#3;G{Ejq4=q_7PBV^qlKE$MzGgA^d?krQNr3?3@grd1D>U)$l z+|fNp9_^BKP4HJX3<;6-WUKrFnlb-ybB+Q>A(|$L^N0M8Kn5l@2XjRJU9!+x1VVHv ziox}tfJm6j@_cd$x^SW2ls7AT>;b82yhn)w9!6Wj`8%8cpVrBQWv2EL)*v7&w76dshoEuHyb_f$7&^NX-*=Op_Q#?WpTH}q zH?Um7&ob>{kQ=JxQh8wW}5CG>A+SzQwofhe!@$a1h3-w54v*O_;( z+WRvqNQTrerseh0?oQdQ-n&~mhB;^(w-CNM1+kX!exEP4D_RX0ns$2c6+tMh~@|kO{CQW01|X7qc<46T)YHJ`+vnH&A{vl~!`<)5KtyxA z842B0g5uY+$tiO1n~CkFSi_mIp7$sEu=MDz76^|Dbto6q<((<`(8#(DwuVAe<$b%h z04DGFcGB$X>V!x$C}INA$fth{!HNQ?^MD$bxy(mhNX^18k1ti^t_t~UmXQQdb#qvP zvuN?I;t9kV#&iyO;LZRpd=Ycj3z7vCUB4{^iar1Q73ucNaM>pP8p<~6jis<#qkCsH1 zjGg#Qz!EWI8g&_USW5h?Pz0M807B0LCYveJgm@96?}mXvJal!{3oU(+PrJe3;A+>K zKP`7#O29X9+_EtZE5`OXW-1-*dxd`W$Q?c2)ZmF&osyAtV|%k3s;ce^TFE~T*Bgd@ zFFVMXsZv43L1>#98Re?L?;XI)7eN-fy0){p&U`OgZ!iQw!J{L`nl&(y4r;a!1sE4p zc59&w#=uA#L3$MV52_ODBXpXm#-$6Rsr2z_lPuz-#K7@gJz}VQmLSS;L5kHMktr)! z_NQrBb;95#2#v=wA%DZh93?d2%#X-5=(Nl{hi%JcfX(sO4NBw@c0g~CIcS1>z!Cwn zt6$DxvQfD3j{M&6}uZchO}@# z#@}7jyp71w#RUG?V=#XE~FquFEnd=o@GZ` zR`htg!cm(*v+xJ~Z z%n7um0OS$h8oha;=h6mo3cc?uuDfzRU(RF1aOf-#hQWfy)6qn?HW)~WOC`&=HHPon zMC^=LD{e>KSRV-e&Gv6 zTQ8WmWd63)hOfF#Kr;j0ykY(*-pIHpB0@0mxts7J4r%q)HV622umnGZ$ywq=eHbAY zmzVn(7Tu7 zS=|kpi^zhzbt8d^fcWC0i13~hiL_x~uDNz=PuBGGPlC4V>>zbq5&Rp23#ycO{(|6g za1_tG>Oi?SnjBYH(^LU3{KV8oYxq?+ocB!N%1(^%h5UXreT9@VFm@wC;5+4e*+Ple zhFxW}WzVx@aJCTX%k}(cGQ}F)QR&AQJ>Iy0NBnuz3z#vEfa(yrm z!4Y4RTmV7VTIt(R*rcRnUad7$80M&!pm}BSn$Z_YkIILIhBk9AdvN_lsDK{W&}+z* zw32BlAFSw|2^1SWC>RdLOhy6p%v9FQ)$F13K`70Oo9-`<8!8;g4QyYbF@J$?BAdIY z=Dkgzob-T#ZMMDuWMGp6C zAGn@x)ymuS?1tAqS|JADw*Cg-kiPgfc^4=)d05xIwt0QarBJ{tpaueA8;tW$NU%+0 z=b!#UPi`5P(A%X}vpa81NE{b$5EjAKIf|)q$}Blih6e-fmUUCaHl|sB?`I39Sm2Y+lhp!vW_MGJOxd+ zAQTSo;AUxG#d>(IS_ZY$g9@xJA&&}MOuumJMX9zpcC4*4w$pS7Ri58C4U>LaRuNi-$Gj+ zzuE%Cp+Q0uv}MlB8`-78e;g{0(u@>c=n#}v2_yxa&NJlp5Ahb!@Kcb-=a09&lqRO%_Krfy;;4#Nl?Uj(^Y%E+ zf}_~2Kg^OoI$c$5h(4Co0i6{K{gpPECAT` zB#T~|J;deV-^vp-cNgA7v0^i>f$;;f=SzNIOnLq#iS(t9f z`?CkIEE2tn-@&_%+=}hMN^oY9Wh{m~T}Yw>(+>!BATjt#qGQp%>TAH1Tue<0yNlZh zhUa_}7q+Hn0D)c5>@crU77^*J{T}3)uYiap<^`D&Dx^hA1qNzS8J$7Xm@?kKRv+Mm zl<$Ddhry7nf*}i3T-ayEn6RZBWLpgxG7&M(P3ck2O=+tGyXjUY0=ui473)&jdkL+s zg8rVU;P?kFAus4rNR3+rFQvC~y;Y0|k987UDWUx=Zf`zh?jEt!<)FEY)~eE$`@=yfWDu^fTH2H02bZ#(pQhYM>Sq^YUQ#4UufPEjWGAbf>kOnIurT)b& z!Snx%Y*Lyc#hu8}`!RmSthl@Kc(x)N&cDyyeN}>^Vv1V8;Zs^3Mok?9hA`v)R*LjErhNgL)bMDl(CLL_l)rq4wYBFLo|=g9EM)=p=eyYjmM zYId&glYP=x<{f78*>*yru*>8}v@9H8RQP=rgAAB(N3Dl}U-pmtL+US^%3iNubi{+A zd7v{ZP0-wrcJ#s_z6eI*8%f6d;8U-u>Ukj%kj}&t1O~wg_@t0}_iN=i3{Wu+ao>C_}> z9cvIv=cTUe;>_$d(*5F995P6n_}$7w2?blRuOS%Un*LO^Bw}q5rak(yT_(0Mn3|sL zS2}jo_<*{$!LV2`djw+%U@&im@6K~mukGQslW#eGkk7b{;HAcyZ}Z#SxNf{LaB35>3y}WO9YD=G2w4V8Zpr7TS1q+&$y+= z8!AfH?VLpY$g`~lr;2j)hfr!DX-<#{>wl@ws7Rv$K}nLaRsc-F2y@^t0`f zV^o7&i%p-OWD}GtYJ4`5V7?lUf7Mf+Wma9*OTXwEc)|usT*ogSLhsJK*)Kikb->>z zy?zcznDj2MWJF!RoP0dQ;;AT}&;9;THKy~)FflOa(UxO7w2{Z^LRk6P13yi(%WCj)R3FC3={-f- z$6kV;kl%uY#K~X6sj@n_tr1*HGJ?W!fHfTIx|GxbL)gEna6xm45?WEx6a_Obp|dV3 zjKGGM04$f1?kfOPekXO;@L!EVoQ zEya+Q|57qW4y;IPUX}&KB{5-Y?2;%)en}kzbgopw(FLgxlu_f`s_u~RVdiL+fO%bx zGdM{?%D=;~B}$eG=T*cb1+CPB^>z{84g0?nV`|(XZi?egU~pgtN41AUS*C-P4oG^v zL-bNR(KxhsTx0NHwn0zNc-EmyC_)Edw8Ix$q`nM`Uz244IK^yJ14{!Pe)O}SJ;Jgj ze}iN=nImuh2Kfuta-&2eP<1>PE{m~A296jxXsraIqIZ`}`h-cnRYe%nJ#LJq?$BS_ zzZ50I&oEiK42&{xzoR{;Wr<};0di9+XWwT7isO-_ZncC$E30a#g{ze2j0-T>w&56# zSku2JuK~eYI_S*pzaN-^*i9Docpfs)0F6$gHaz*~{cb%B_lJX*%=+0=QeCQbP&bjp z1TUW8jQ!XX)pCA+Mg-h!0;Zr`I`Kc;9ieMpOY^ zz=GT1b=TSCiVsBi_AK!Dg`|}9?xBRy^ z%o#N(5h3wnQzV%19z)j6!zw=^V zh?Ahriae{F>5mcm;}oWA@Q>514!uBU3JSk8uV+s~lG_+U|8Q>YugBSh@bu4@mE?Q$ z=4qqQ=UuHLXWAtER;6wN@#A{t;8Mk-e)dFm_JM8zH5quY+drgDM~f$uK1ufLxzUp+ zsLDpC{KQv!=dZ?>s>`FUk_%^IJO3mXLaTknCUs-1iUIHRG;+BfI^){6`_$0mZ_x2+ z(CGrUi8W;LH*RpQtz+@Nn!jAno;k>5Kdzq^Ugl`EC<2f&!Q`--_+Ww^Gr;*yLB4B; z78FjIL)86vff|w~efteLe$4^x_1#*EzbfJfE%=Vmd-e?kpZN&>d7~TMw?%T3{!7QD zuZ>$+*pXH;a`anrumnu6zi21djGTE7?O}PPA**7`X+I#$YBB}}gp*v$OFCl1@E5*K zh=Ryr84loD|2XImOAiuuyt067uV1DiAGcT3vZ3`N$k*J*Yju{ z5a|q$J5^^ZiT(>{e4*JX2Dv8INKb)I)k+RihR_N%cXODW7<`c<5h>4qZ@+ z4nI7OgtfMEnyP@&B~Yf5 z)5hiHs(EgUq*OAo)sX&LnddN$wC`EE6SCCI|1FRA+>cUR=1^la1w8Gp!g z|FZ9f(7%b%)_$g$F9l4FR=V+qPe^DaYM;ldl_*)D9m<}SOENuR*JI?IA@Qpgui%WR zPfmtw<9|&b9J7u%x6C~junYy}>)#Li!_4(>Bi~A{dYY%s1DaZC9t&AR*=X3FhKqSI z+&2cS1oL~1L3?QF?eeC15werQ_1$5aHeTFwddHmUqR-iXIc$+rDR$3T`xVYJ$D5do zuA04nDV!VBeqUAkiN_@yPWcNe@DSlr#h=y?x4IcEp#G0*LXm|mV52D9%G95D8GS4CFWU+@oGQbJ$&SkdcIv~`Nss|<*8=6e zy#Dqi?p!OLpbz){q#baQFGxCxQoq5OryHa$r5)BNoYPGZY6F*LpFs?fc_%^rmRcCs zZrRtnO*++W9_Bl`8dMTUy6Kp&Ic5KObUo-<5Do0ByYD#K{nRJk{t0hQR8|q}Kqb@^x5Lu)4467Wo0Y~fa#}D{ zO-Ep_D&7%l+ZisOK@yNY{gPg`w-`P_+!ZT#qOR$(7$vc3ohoiIXr*ssoQN|1y2-2P zdd>gEXq-igVI-lf-e8zuO?G8nvP3TyFVe_Hbo%^yf-M5Y1c1c}8_6le1@8ULe=l{4 zL^vXPiMA1=m5#8pJFWTVJ7h}1(s1^2?_SamDpP6+Thy_Bamvc;Sq!OEV&73wc81vtDUl0~J7elhq3}CHXSj+b7iik1t_41xtR{ zX!vvCAS$<;H?0JD&=7qwFc{b~@&vcW&v@u{S9p#aV0q}S6&jcol#`mRVsO{(GaB!b{AQCIl;<`$m4M^zQaxV;do_W*;8A^26~e5SiP#<6{+6L$3eakpVWw zQURkc16q8J210zUMn+Ir5d%XU3MLXTP!w3Vg7D9wu;M*;&qp3rh1O+Qfh3x^ALY?A zZ*4^mjUO1@X&7dN0Pd>DJ!@GY zF@i(`5F+(#VYm5V4LVU@MyMaJH3Z0$0iL8Ru`Xv7d=LNXq08*2%;WLF5mXZtiG;P2 zfM6W<0|uJ~W-2|AsZ5*G#i@aasKj#9;!Zdz&(oG*zH zL57GR4Ei*HvY|v6rQIBm$}Wu|5Kl&-qdW;kO{cYahM^YbZ?@RXI+}t_4S*Ay+||Hb zV*cy;Cq|zY*>CsW=PzvNz+CSK65;4dt8a2=>+EA%aYzv`}pQ3F9;wf$B<^uXD zt>VzPZG?)THI*w&s!g2p%AMB>;dg~=iaoHo#iU9XreoK-! z6)LZJiun(;Z@+f`J& z1Hwr*Zbe1F>nLTEi&wtrmwk?Pc929QOf-YZcEu%V4WW_Nd~uqzFLF?p@+tBV>RLu? zqiD`O!oadh4&rFB(8i!!4A@_a!lL0yC|*Yr8`XWLVD!ZfqvURvU2Q%>-L0xv7S_p4 z*`BS9fy={D1WImog1nT(c#V#JZr83DwZ|@>6r2b3-rq{BmphE_KAy*Voi3#4-ypUT zYsTo~N}6f14cO@O-+@7i7GtBI#y}rd)X1!c}6w?}F!B*}d-Q3l#tF5k5PuwQK3 zDTG~BG_rOrJG+bNc)@z{*~`}Dhpn1fH%`nEl)0?iL6OX+WFvTDIxjI$cB?i*j~GOGXg$0ZPDImk)LlT1_{d@@fdLTUvd`q#<*jC(^ut!HZx} zNbj3R4!DRU6J{Gx>oAt7wai33YhuUZF>wpFa}o94Cv(Hn2Ifbpu;r3jzIuUTYg(1Y zG^gCtEQr|_Jn4Wvu4I?4f~p1~B5L`{Uj4FoUAy7Up~=!^59a!ZHBd0UDyjRv0W$s* zG`#ac{Re2U8XP*GF?S;yH~3V;D=a^hjp#dUnt&ONh)4?}r*_UZg2kd6#>X!Mmhsty zr3znY#Q!H{=r5b>6BXWsJEuh>h36>w41pA7g`V)Q8=A47+cm@ zsH?)I4m~}j$S@u>4aa(Q8AXXppkM|Uf$mFi8q}pH0Yos!I~Q@V>xy8o1LMX65;R2> zlhbDvfR?s?jdzEsJY3MY`oa6=Wz{yJ9SU0K<^MHWpfwK?cW~j?2u5;iMPD%e&~&?m z)`T3S4+0N$*jRtVR4#|}GVxD$a^4X|KzT-y0eUfrU_|ps0m$V(9s1s*(ZLX}9cB(1 zHZQM#B_FH-A;7@$w~4)+i*7`3MLdXCiZA`x2F4jYlnvNn3t zHfC;Ejp%qL%_C1nD^AL*e4}M8NHkT@9YoV-6+%y`f0Z7H4*NUtytj+Ll;M0y5;62{*UywG_`1&`q(6tYX z=(SHEQ0<`u(gk+lSKtNGg~wCh8rb#6LhKv{-9EHgG$HvZhkEod4tVot%`Nz3 z2k|9JV)kOg+EwWOy73SrS6rDoF!}mQ5{qJ%^Ly-Y>FJZt^(-pX)#=hbA8SlnosLCc zE~{%bs36CpswA48+B9B_X zn$3!CE`u@!>=Q8!@Bwa@JX*4*1pG zC4EsuXz3lnW34<5NFzwU%adUu)-Fy`XS5C0qbvtW&MzYJ&qKuGB0=6BdJJoXW#>F7 z0-i&EZ+I>Ml{QX={xk%~;d@li`?7T}upI<1%{n?+zK3^j zynW2^{)sDT139p(i+yVv%4uP4{dx)iE%8V}{tpsQv9ZJdEs5tOd-q=w4-S3WW8MF` z#M9sq7huQw8{+@5#M7qvKTAB`8KM7+#6vP4cg&e!c|5>kjOZ@OiD60wu(xQUr;Jf! z1L4vvtO8lLVC@OxQ57-;pG9wsht0e7$D{_C0LAJSdGSNJLf(Jta=Ec$nJlMEyorgwSq#| z^IsCr_36j|QQ~om;rJgUo_K4LVH>2)?2E8<__p-_mUtK-fD%s|3Q*!`F$9?qQ zC7#c||7(dyp$(c4F7^ML#6xDv)_*pso#nH%pLehs<3^3RGrzIMlE1az#DItCR#!b! z>ccXe#mP|Pbk0wV2k2@n5IvV0R1TSX6myUV_y{{I504zykXY26cb?oA@&CzinHkSZ z3Zs>boQ7Iv`w$`{08_WW#2Rc6rUZ+x?rHjGsI&)9y9C5|2d+b`wG1+==*+S`KR`$H?U(YY{<{sNWW+K-Hu%vzyrGO_(Hztv!2#Ilq7@DWVq9P*94NR3z<;Su^;cU<$WuH=Px zUtBXGF-afk?+yQd@2?~VQ@wvEk7)S7U&=$3efMva zXA1Ui5lT%TcsJBe37kgQJQQO4Vs=wmh?&0md__;u z!;n)sF#mb1499G-X8yx{-jixUv z9xhz(arDs9?eP21&7+1i!eiKh_f~h+AF_{Ym!*H&_C;YTOGYA{ z~HI1|5gstP*toN9;)Y1XIww|ln4@fcY=4ECI7gN7tr@$DYfkKRw#mpiGR-{usF z;iG=ucm&zKZAuL3cbm~1B_dqPyW!RQ{bFRe*JCaFH93ah}43j-sSAB&*BLEpr)%3_uNPEj;N922fVql0Ii|X zNzGaaK(%5Hn0~W?7@)g}M)Lu0ILq>^7lXak_*k(m@xTx$epY5nChKs(W(j-qRCMUh zp)5bAO&+;zyC5W(A^(MVDqv{|NUC#sq031huf3|SsVP$6G91evMNPV5eMHFie#hb~ zxLdc~D8z)W)hr8muC)f7u$(-~@hZHywucdYmAI3I8))mrINgFqmYsP)Uru_* z6M0>mWDT)BN*(6NJX1c?4v(AWSQj0Fbu9ChQZ`Y*aJoQ1WL~t&tC~DY`Ra(65_i`T zeO!7ZxXa4e-dKSf`7xV@6tN_m(g#t8iGQnNO(nc4XGizwEHNh6=l%j>s>+1koB&fo zCW}laa6p5)-?)N9|5O2Olu!}IHq=;62q-GulbN^|TCTB37WKTI|5}$U7D&<8Apu5_ zUk*0fLEIUO`XKB{{2Ot%1hGC?4jGm?)<10!jSG}Dn;AAw=IDMWsN)%phyR&!`e%#~ ztAsfy)am3lRkoa_y*GS(^g=pOVM+4NDM`MfNMT*Bfy~SO&H$m0^xM46$#)^(S#M+F75>&NHE1kuiA+ z?iq>BfydrHbDJYV&=0MQlM5r*kv-iCDhG+l{qH*%q?dvI=)opOa5|#_S=bM&EYkHS zql#SOKFsAty3_B1f8JfnS7BymHh%S21vPHq#rVRxpC<4z3UbMJtfjg!C9hOsR0!th z^%_I<)5E9p504Ryr5o~m=h=3hwBIf$rq7c%T>=&<=-{DHsC{0UnntR7EJg{)G1#?6 z7})F{(W2iw03Ap+?dk8@I z!HU9fTl`rVk z?&|i%AS@T3Lc8I@u&K|}r|hGW=k3KN0D7^acT3Nhpk>_KWjd!g1l$6)J7kj9Dlfl&G0QnDbwx38aZf6WWNG{vAWW;#3~p~t62r45jP?V% zuNLIjmJxXPOe57Vq`;F{9V%;R!qZ}cV6yZcg1+@7M+(?d0x?d_98SL z-ZYSj2jxyijUN;>%~9L#_Rfak2hQ`+RVi z%iPe+Rvy!<2zoR%8cvE%qeTwp=$8hu%pWs;=*yDEQlxJ~rMq<$-h9dL6GY8AHs>zb z^%g(-ljwL2oudLVmnn>pW$^LG6- zsSLNJH(Xe&svqvWuklqHhU=6Mcu0h+67+=cNr|b}AGDe;WmW6l+(thzr8V10A4*J4 zPJn8Vkm^n*-@jo=K`WOJS7~MtfN7!Hh~(Pk=>W(pYMa=Pbj4D$-b>(xtHT33U8!lV z*5|t)GS$KA{g!gRGKD$wu+2~u@v^(~4cG~m_Zh?2^U}x69uthn^9(Z=A^5*M%SrKqqTWb3_hUV9AHdvEXWB?y~Jn8m^BJVRW82SzWBY?m8k5BD|pR)PJ zA`Yj^Fh9b=W%N2Pe8MG)kD`>DkTHoT&xSAR$9){@E3V`eEMNbyH1V`5(yA_brHw@D z#BjKkdp9l-)ur%0zI#Q95?g$RWs+>65n6GGf@ZP7n0`S3Ahsj`T zT13c1V)cB!+P!>}LLu3fCEt8+gH8Y+neo{_?26dKo?FG0_xcn5{$WM9zfzyrhHN2 z2n%DAo!D~JD34;DKrm-4#SBd+QqK@QhD}ga3x|@iwt~(PrEXwNBSMovy=Op6vOGi2 z7Rg0!s3c-pLR1T2k8+5m?WqW}G%>)xxC=rv6Mdfc?7pDs%C?4u4LlLUeXTffqi{6J z3{&Bxau>(#NpYVhljK`>^$8B}mXa{L?C14j&PL{Zx>55-B=8H;aNWxhLEv_6SFgnu z(hBC?(*d3E_>QqF@GYibn}j>E#O{OatKxSIZes38{3z++J*IyS$-mBd6@45~o&DrP z{u&Q9+J*hxe)8kguDtYKI!~p@*I04f)(mo= zk|r-oeNW0JQAr1@SS?XB_cLM1Rwhc?h7Ps#~d4;YFUhp;ztSa18~QUSHs2*Z+gRFtX!uUm53dDzBbPX_%&E z2XZ8f_7FzDN z5CBZ`6#MaKRL$SAT!M>7nhdnwiffeIqu=f<%T1~IC_+YxuD?8mQS%MM0=N3Idoclo z|2UoYB>6_oSMukUuiv<7?1FyU5zCHua=X8Va6fddbAmCCU7OnryMXDvx6YC9Bw9Fn zcg=a3{ss#de%~F((u0?%#t|dnhnW0e|1iac>;CgKpO$T2%RYL-{I1s9{C7ST+RW1e zV=c~#1WfIGy}EA^!_O^aY^?=>RcaZ6ed>GHZJ{_1fY)lHf<=1~c9fLr2KG3J86uUT5B2e{3wigO_?I$%M5%i6MVKjD~ zLA26@9-^^S(gzhNe^qB+{4!;fUeC!hNNrbJ8mcf~F99ac?bh*%f(KKCNJ^_Yx@4xu zZgeUEXN$2PL?g@ug=qsigbz20?-sU1Ep67I7V7^i?P*f6zk1FVEJBS7YOEXDz8?AX zi9$7iqO-{Ia?=2skvx)M!R`9&At%mi^5o|7WZ$Cv*hw^zRGcpNZl ze14+Ggz@gHyTmwKLQGupFrW8L5T8lr? zd2f8R!rtf^I5nwR^yhu@ib;3$xQL&A_HIGj?=5GJY#Eel*c1whm)@iUpt5!sAmigU zRc7e&15_l(&7^9WW9GQ5C%{SlK0VkCoUn%H z6CBZKwUKVKH4=9xT+-2+Khw7M_ipMhtqO3L#F}qSFXBfGhfu|+g^EO#=N^@w+^jY+ zCycSJUK&G+%vWRt*YU=CFC~9q=FSIhr07Np{Gv7q3%4kDj5%|naL=Q)l zXCYHDF06gzX-kX>UgeiyXoyYnLB6ux!NV(I&l zIx|pnXLsHO1)+^0YO{d3KiFI&yeY|?H{Bj`J#H5RK$)@g1whqhW=?~i2aXtRz9fj! zvuXE_1XhnywHT}#>u%Ejj9{#J3Frkd|`ljW;%Dh z29x&n!#6rkK1?N&-8y8z3&0OaM`W;%Dxxuh5S#sFhAV5^9H%<2QWi^;p?qSftz36w zM-Pgd->5`%L!5!FjB->kU?mjtk3btg+=>Lcs>eT^T0+>qx+veySI=l&gZT zwP;Q%_?!W(J=)e}mirxMMW8wO{9ufT)zd;IJG#;A1YuT2Jac9k)?x0C`IyEirnYzy zA+w%9#c+Sk<-6*w(DpCQ?T$-W;xT2_>}0wECCkD`v0bl7k&HnLBk|KW1A1B$vCv-v zvK4H@+|!=Hhgl~f-u#e0Z>Ac*73ZlU)>_?A9W=ISeW&`~{UpC-8T00MlH>RIM~Trl zDt``V{i*R|`qkXeneQU@jQnlC)$ix0g8Hlz?&hDGz^V^br=ve>f^}MdUQg{JyJK4? joge-F{HgsT9Y-u22(b|K2Lkv{$A5SQ^{GKnU_kyiyoj#O literal 0 HcmV?d00001 diff --git a/charts/airlock/microgateway-cni/4.4.0/.helmignore b/charts/airlock/microgateway-cni/4.4.0/.helmignore new file mode 100644 index 000000000..8561d2892 --- /dev/null +++ b/charts/airlock/microgateway-cni/4.4.0/.helmignore @@ -0,0 +1,27 @@ +# 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/ + +# Helm unit tests +/tests +/validation diff --git a/charts/airlock/microgateway-cni/4.4.0/Chart.yaml b/charts/airlock/microgateway-cni/4.4.0/Chart.yaml new file mode 100644 index 000000000..883012b58 --- /dev/null +++ b/charts/airlock/microgateway-cni/4.4.0/Chart.yaml @@ -0,0 +1,43 @@ +annotations: + artifacthub.io/category: security + artifacthub.io/license: MIT + artifacthub.io/links: | + - name: Airlock Microgateway Documentation + url: https://docs.airlock.com/microgateway/4.4/ + - name: Airlock Microgateway Labs + url: https://play.instruqt.com/airlock/invite/hyi9fy4b4jzc?icp_referrer=artifacthub.io + - name: Airlock Microgateway Forum + url: https://forum.airlock.com/ + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Airlock Microgateway CNI + catalog.cattle.io/kube-version: '>=1.25.0-0' + catalog.cattle.io/release-name: microgateway-cni + charts.openshift.io/name: Airlock Microgateway CNI +apiVersion: v2 +appVersion: 4.4.0 +description: A Helm chart for deploying the Airlock Microgateway CNI plugin +home: https://www.airlock.com/en/microgateway +icon: file://assets/icons/microgateway-cni.svg +keywords: +- WAF +- Web Application Firewall +- WAAP +- Web Application and API protection +- OWASP +- Airlock +- Microgateway +- Security +- Filtering +- DevSecOps +- shift left +- CNI +kubeVersion: '>=1.25.0-0' +maintainers: +- email: support@airlock.com + name: Airlock + url: https://www.airlock.com/ +name: microgateway-cni +sources: +- https://github.com/airlock/microgateway +type: application +version: 4.4.0 diff --git a/charts/airlock/microgateway-cni/4.4.0/README.md b/charts/airlock/microgateway-cni/4.4.0/README.md new file mode 100644 index 000000000..f72f069cf --- /dev/null +++ b/charts/airlock/microgateway-cni/4.4.0/README.md @@ -0,0 +1,137 @@ +# Airlock Microgateway CNI + +![Version: 4.4.0](https://img.shields.io/badge/Version-4.4.0-informational?style=flat-square) ![AppVersion: 4.4.0](https://img.shields.io/badge/AppVersion-4.4.0-informational?style=flat-square) + +*Airlock Microgateway is a Kubernetes native WAAP (Web Application and API Protection) solution to protect microservices.* + + + + + Microgateway + + +Modern application security is embedded in the development workflow and follows DevSecOps paradigms. Airlock Microgateway is the perfect fit for these requirements. It is a lightweight alternative to the Airlock Gateway appliance, optimized for Kubernetes environments. Airlock Microgateway protects your applications and microservices with the tried-and-tested Airlock security features against attacks, while also providing a high degree of scalability. +__This Helm chart is part of Airlock Microgateway. See our [GitHub repo](https://github.com/airlock/microgateway/tree/4.4.0).__ + +### Features +* Kubernetes native integration with sidecar injection and Gateway API support +* Reverse proxy functionality with request routing rules, TLS termination and remote IP extraction +* Using native Envoy HTTP filters like Lua scripting, RBAC, ext_authz, JWT authentication +* Content security filters for protecting against known attacks (OWASP Top 10) +* Access control using OpenID Connect to allow only authenticated users to access the protected services +* API security features like JSON parsing, OpenAPI specification enforcement or GraphQL schema validation + +For a list of all features, view the **[comparison of the community and premium edition](https://docs.airlock.com/microgateway/latest/#data/1675772882054.html)**. + +## Documentation and links + +Check the official documentation at **[docs.airlock.com](https://docs.airlock.com/microgateway/latest/)** or the product website at **[airlock.com/microgateway](https://www.airlock.com/en/microgateway)**. The links below point out the most interesting documentation sites when starting with Airlock Microgateway. + +* [Getting Started](https://docs.airlock.com/microgateway/latest/#data/1660804708742.html) +* [System Architecture](https://docs.airlock.com/microgateway/latest/#data/1660804709650.html) +* [Installation](https://docs.airlock.com/microgateway/latest/#data/1660804708637.html) +* [Troubleshooting](https://docs.airlock.com/microgateway/latest/#data/1659430054787.html) +* [GitHub](https://github.com/airlock/microgateway) + +# Quick start guide + +The instructions below provide a quick start guide. Detailed information are provided in the **[manual](https://docs.airlock.com/microgateway/latest/)**. + +## Prerequisites +* [helm](https://helm.sh/docs/intro/install/) (>= v3.8.0) + +## Deploy Airlock Microgateway CNI +1. Install the CNI Plugin with Helm. + > **Note**: Certain environments such as OpenShift or GKE require non-default configurations when installing the CNI plugin. For the most common setups, values files are provided in the [chart folder](/deploy/charts/airlock-microgateway-cni). + ```bash + # Standard setup + helm install airlock-microgateway-cni -n kube-system oci://quay.io/airlockcharts/microgateway-cni --version '4.4.0' + kubectl -n kube-system rollout status daemonset -l app.kubernetes.io/instance=airlock-microgateway-cni + ``` + ```bash + # GKE setup + helm install airlock-microgateway-cni -n kube-system oci://quay.io/airlockcharts/microgateway-cni --version '4.4.0' -f https://raw.githubusercontent.com/airlock/microgateway/4.4.0/deploy/charts/airlock-microgateway-cni/gke-values.yaml + kubectl -n kube-system rollout status daemonset -l app.kubernetes.io/instance=airlock-microgateway-cni + ``` + ```bash + # OpenShift setup + helm install airlock-microgateway-cni -n openshift-operators oci://quay.io/airlockcharts/microgateway-cni --version '4.4.0' -f https://raw.githubusercontent.com/airlock/microgateway/4.4.0/deploy/charts/airlock-microgateway-cni/openshift-values.yaml + kubectl -n openshift-operators rollout status daemonset -l app.kubernetes.io/instance=airlock-microgateway-cni + ``` + > **Important:** On OpenShift, all pods which should be protected by Airlock Microgateway must explicitly reference the Airlock Microgateway CNI NetworkAttachmentDefinition via the annotation `k8s.v1.cni.cncf.io/networks` (see [documentation](https://docs.airlock.com/microgateway/latest/#data/1658483168033.html) for details). + +2. (Recommended) You can verify the correctness of the installation with `helm test`. + ```bash + # Standard and GKE setup + helm upgrade airlock-microgateway-cni -n kube-system --set tests.enabled=true --reuse-values oci://quay.io/airlockcharts/microgateway-cni --version '4.4.0' + helm test airlock-microgateway-cni -n kube-system --logs + helm upgrade airlock-microgateway-cni -n kube-system --set tests.enabled=false --reuse-values oci://quay.io/airlockcharts/microgateway-cni --version '4.4.0' + ``` + ```bash + # OpenShift setup + helm upgrade airlock-microgateway-cni -n openshift-operators --set tests.enabled=true --reuse-values oci://quay.io/airlockcharts/microgateway-cni --version '4.4.0' + helm test airlock-microgateway-cni -n openshift-operators --logs + helm upgrade airlock-microgateway-cni -n openshift-operators --set tests.enabled=false --reuse-values oci://quay.io/airlockcharts/microgateway-cni --version '4.4.0' + ``` + + Consult our [documentation](https://docs.airlock.com/microgateway/latest/#data/1699611533587.html) in case of any installation error. + +## Support + +### Premium support +If you have a paid license, please follow the [premium support process](https://techzone.ergon.ch/support-process). + +### Community support +For the community edition, check our **[Airlock community forum](https://forum.airlock.com/)** for FAQs or register to post your question. +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| affinity | object | `{}` | Custom affinity for the DaemonSet to only deploy the CNI plugin on specific nodes. | +| commonAnnotations | object | `{}` | Annotations to add to all resources. | +| commonLabels | object | `{}` | Labels to add to all resources. | +| config.cniBinDir | string | `"/opt/cni/bin"` | Directory where the CNI plugin binaries reside on the host. This path can either be found in the documentation of your Kubernetes distribution or CNI provider. It can also be queried by running the command `crictl info -o go-template --template '{{.config.cni.binDir}}'` on your Kubernetes node. | +| config.cniNetDir | string | `"/etc/cni/net.d"` | Directory where the CNI config files reside on the host. This path can either be found in the documentation of your Kubernetes distribution or CNI provider. It can also be queried by running the command `crictl info -o go-template --template '{{.config.cni.confDir}}'` on your Kubernetes node. | +| config.excludeNamespaces | list | `["kube-system"]` | Namespaces for which this CNI plugin should not apply any modifications. | +| config.installMode | string | `"chained"` | Whether to install the CNI plugin as a `chained` plugin (default, required with most interface CNI providers), as a `standalone` plugin (required for use with Multus CNI, e.g. on OpenShift) or in `manual` mode, where no CNI network configuration is written. | +| config.logLevel | string | `"info"` | Log level for the CNI installer and plugin. | +| fullnameOverride | string | `""` | Allows overriding the name to use as full name of resources. | +| image.digest | string | `"sha256:e9d711dfe75d515ad8bc5ba5e668e7a26c063bd6a291305aac458c2cbd3945f2"` | SHA256 image digest to pull (in the format "sha256:7144f7bab3d4c2648d7e59409f15ec52a18006a128c733fcff20d3a4a54ba44a"). Overrides tag when specified. | +| image.pullPolicy | string | `"IfNotPresent"` | Pull policy for this image. | +| image.repository | string | `"quay.io/airlock/microgateway-cni"` | Image repository from which to pull the Airlock Microgateway CNI image. | +| image.tag | string | `"4.4.0"` | Image tag to pull. | +| imagePullSecrets | list | `[]` | ImagePullSecrets to use when pulling images. | +| multusNetworkAttachmentDefinition.create | bool | `false` | Whether a NetworkAttachmentDefinition CR should be created, which can be used for applying the CNI plugin to Pods. | +| multusNetworkAttachmentDefinition.namespace | string | `"default"` | Namespace in which the NetworkAttachmentDefinition is deployed. Note: If namespace is set to a custom value, referencing the created NetworkAttachmentDefinition from other namespaces may not work if Multus namespace isolation is enabled. https://github.com/k8snetworkplumbingwg/multus-cni/blob/v4.0.2/docs/configuration.md#namespace-isolation | +| nameOverride | string | `""` | Allows overriding the name to use instead of "microgateway-cni". | +| nodeSelector | object | `{"kubernetes.io/os":"linux"}` | NodeSelector to apply to the CNI DaemonSet in order to only deploy the CNI plugin on specific nodes. | +| podAnnotations | object | `{}` | Annotations to add to all Pods. | +| podLabels | object | `{}` | Labels to add to all Pods. | +| privileged | bool | `false` | Whether the DaemonSet should run in privileged mode. Must be enabled for environments which require it for writing files to the host (e.g. OpenShift). | +| rbac.create | bool | `true` | Whether to create RBAC resources which are required for the CNI plugin to function. | +| rbac.createSCCRole | OpenShift | `false` | Whether to create RBAC resources which allow the CNI installer to use the "privileged" security context constraint. | +| resources | object | `{"requests":{"cpu":"10m","memory":"100Mi"}}` | Resource restrictions to apply to the CNI installer container. | +| serviceAccount.annotations | object | `{}` | Annotations to add to the ServiceAccount. | +| serviceAccount.create | bool | `true` | Whether a ServiceAccount should be created. | +| serviceAccount.name | string | `""` | Name of the ServiceAccount to use. If not set and create is true, a name is generated using the fullname template. | +| tests.enabled | bool | `false` | Whether additional resources required for running `helm test` should be created (e.g. Roles and ServiceAccounts). If set to false, `helm test` will not run any tests. | + +## License +View the [detailed license terms](https://www.airlock.com/en/airlock-license) for the software contained in this image. +* Decompiling or reverse engineering is not permitted. +* Using any of the deny rules or parts of these filter patterns outside of the image is not permitted. + +Airlock® is a security innovation by [ergon](https://www.ergon.ch/en) + + + + + + + Airlock Secure Access Hub + + diff --git a/charts/airlock/microgateway-cni/4.4.0/gke-values.yaml b/charts/airlock/microgateway-cni/4.4.0/gke-values.yaml new file mode 100644 index 000000000..d6d5c21d1 --- /dev/null +++ b/charts/airlock/microgateway-cni/4.4.0/gke-values.yaml @@ -0,0 +1,4 @@ +# values for deploying on GKE + +config: + cniBinDir: "/home/kubernetes/bin" diff --git a/charts/airlock/microgateway-cni/4.4.0/openshift-values.yaml b/charts/airlock/microgateway-cni/4.4.0/openshift-values.yaml new file mode 100644 index 000000000..3b1d6cccd --- /dev/null +++ b/charts/airlock/microgateway-cni/4.4.0/openshift-values.yaml @@ -0,0 +1,15 @@ +# values for deploying on OpenShift + +rbac: + createSCCRole: true + +privileged: true + +multusNetworkAttachmentDefinition: + create: true + namespace: default + +config: + installMode: "standalone" + cniNetDir: "/etc/cni/multus/net.d" + cniBinDir: "/var/lib/cni/bin" diff --git a/charts/airlock/microgateway-cni/4.4.0/questions.yml b/charts/airlock/microgateway-cni/4.4.0/questions.yml new file mode 100644 index 000000000..73ed44d64 --- /dev/null +++ b/charts/airlock/microgateway-cni/4.4.0/questions.yml @@ -0,0 +1,18 @@ +questions: + - variable: config.cniNetDir + required: true + type: string + label: CNI Network Configuration Directory + group: "CNI Settings" + description: "Directory where the CNI config files reside on the host. This value depends on the kubernetes distribution and interface CNI Provider used. It can be fetched by running `crictl info -o go-template --template '{{.config.cni.confDir}}'` on your kubernetes host." + - variable: config.cniBinDir + required: true + type: string + label: CNI Plugin Binaries Directory + group: "CNI Settings" + description: "Directory where the CNI plugin binaries reside on the host. This value depends on the kubernetes distribution and interface CNI Provider used. It can be fetched by running `crictl info -o go-template --template '{{.config.cni.binDir}}'` on your kubernetes host." + - variable: config.installMode + required: true + label: CNI Plugin Installation Mode + group: "CNI Settings" + description: "Whether to install the CNI plugin as a `chained` plugin (default, required with most interface CNI providers) as a `standalone` plugin (required for use with Multus CNI, e.g. on OpenShift) or in `manual` mode, where no CNI network configuration is written. Please refer to the CNI installation documentation (https://github.com/airlock/microgateway?tab=readme-ov-file#deploy-airlock-microgateway-cni) to correctly setup the CNI Plugin for your environment." diff --git a/charts/airlock/microgateway-cni/4.4.0/templates/NOTES.txt b/charts/airlock/microgateway-cni/4.4.0/templates/NOTES.txt new file mode 100644 index 000000000..bb94ff521 --- /dev/null +++ b/charts/airlock/microgateway-cni/4.4.0/templates/NOTES.txt @@ -0,0 +1,15 @@ +Thank you for installing Airlock Microgateway CNI. + +Please ensure that the helm values'.config.cniNetDir' and '.config.cniBinDir' are configured for your Kubernetes distribution. +For further information, consider our manual https://docs.airlock.com/microgateway/{{ include "airlock-microgateway-cni.docsVersion" . }}. +The chapter 'Setup > Installation' describes how to set those settings correctly. + +Further information: +* Documentation: https://docs.airlock.com/microgateway/{{ include "airlock-microgateway-cni.docsVersion" . }} +* Airlock Microgateway Labs: https://play.instruqt.com/airlock/invite/hyi9fy4b4jzc?icp_referrer=helm + +Next steps: +* Install Airlock Microgateway (if not done already) + https://artifacthub.io/packages/helm/airlock-microgateway/microgateway + +Your release version is {{ .Chart.Version }}. \ No newline at end of file diff --git a/charts/airlock/microgateway-cni/4.4.0/templates/_helpers.tpl b/charts/airlock/microgateway-cni/4.4.0/templates/_helpers.tpl new file mode 100644 index 000000000..996491a87 --- /dev/null +++ b/charts/airlock/microgateway-cni/4.4.0/templates/_helpers.tpl @@ -0,0 +1,101 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "airlock-microgateway-cni.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Convert an image configuration object into an image ref string. +*/}} +{{- define "airlock-microgateway-cni.image" -}} + {{- if .digest -}} + {{- printf "%s@%s" .repository .digest -}} + {{- else if .tag -}} + {{- printf "%s:%s" .repository .tag -}} + {{- else -}} + {{- printf "%s" .repository -}} + {{- end -}} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 50 chars because some Kubernetes name fields are limited to 63 chars (by the DNS naming spec) +and the longest suffix is 13 characters. +If release name contains chart name it will be used as a full name. +*/}} +{{- define "airlock-microgateway-cni.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 50 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 50 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 50 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "airlock-microgateway-cni.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "airlock-microgateway-cni.labels" -}} +helm.sh/chart: {{ include "airlock-microgateway-cni.chart" . }} +{{ include "airlock-microgateway-cni.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- with .Values.commonLabels }} +{{ toYaml .}} +{{- end }} +{{- end }} + +{{/* +Common labels without component +*/}} +{{- define "airlock-microgateway-cni.labelsWithoutComponent" -}} +{{- $labels := fromYaml (include "airlock-microgateway-cni.labels" .) -}} +{{ unset $labels "app.kubernetes.io/component" | toYaml }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "airlock-microgateway-cni.selectorLabels" -}} +app.kubernetes.io/component: cni-plugin-installer +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/name: {{ include "airlock-microgateway-cni.name" . }} +{{- end }} + +{{/* +Create the name of the service account to use for the CNI Plugin +*/}} +{{- define "airlock-microgateway-cni.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "airlock-microgateway-cni.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{- define "airlock-microgateway-cni.isSemver" -}} +{{- regexMatch `^(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-]+)*))?$` . -}} +{{- end -}} + +{{- define "airlock-microgateway-cni.docsVersion" -}} +{{- if and (eq "true" (include "airlock-microgateway-cni.isSemver" .Chart.AppVersion)) (not (contains "-" .Chart.AppVersion)) -}} + {{- $version := (semver .Chart.AppVersion) -}} + {{- $version.Major }}.{{ $version.Minor -}} +{{- else -}} + {{- print "latest" -}} +{{- end -}} +{{- end -}} diff --git a/charts/airlock/microgateway-cni/4.4.0/templates/clusterrole.yaml b/charts/airlock/microgateway-cni/4.4.0/templates/clusterrole.yaml new file mode 100644 index 000000000..ef88ac783 --- /dev/null +++ b/charts/airlock/microgateway-cni/4.4.0/templates/clusterrole.yaml @@ -0,0 +1,22 @@ +{{- if .Values.rbac.create -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "airlock-microgateway-cni.fullname" . }} + labels: + {{- include "airlock-microgateway-cni.labels" . | nindent 4 }} + {{- with .Values.commonAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch + - patch +{{- end -}} diff --git a/charts/airlock/microgateway-cni/4.4.0/templates/clusterrolebinding.yaml b/charts/airlock/microgateway-cni/4.4.0/templates/clusterrolebinding.yaml new file mode 100644 index 000000000..04f87cb0f --- /dev/null +++ b/charts/airlock/microgateway-cni/4.4.0/templates/clusterrolebinding.yaml @@ -0,0 +1,20 @@ +{{- if .Values.rbac.create -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "airlock-microgateway-cni.fullname" . }} + labels: + {{- include "airlock-microgateway-cni.labels" . | nindent 4 }} + {{- with .Values.commonAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "airlock-microgateway-cni.fullname" . }} +subjects: + - kind: ServiceAccount + name: {{ include "airlock-microgateway-cni.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +{{- end -}} diff --git a/charts/airlock/microgateway-cni/4.4.0/templates/configmap.yaml b/charts/airlock/microgateway-cni/4.4.0/templates/configmap.yaml new file mode 100644 index 000000000..b880116ef --- /dev/null +++ b/charts/airlock/microgateway-cni/4.4.0/templates/configmap.yaml @@ -0,0 +1,22 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "airlock-microgateway-cni.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "airlock-microgateway-cni.labels" . | nindent 4 }} + {{- with .Values.commonAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +data: + plugin-conf.json: |- + { + "type": "{{ include "airlock-microgateway-cni.fullname" . }}", + "debug": {{ eq .Values.config.logLevel "debug" }}, + "logFilePath": "/var/log/{{ include "airlock-microgateway-cni.fullname" . }}.log", + "kubernetes": { + "kubeconfig": "{{ .Values.config.cniNetDir }}/{{ include "airlock-microgateway-cni.fullname" . }}-kubeconfig", + "excludeNamespaces": {{ toJson .Values.config.excludeNamespaces }} + } + } diff --git a/charts/airlock/microgateway-cni/4.4.0/templates/daemonset.yaml b/charts/airlock/microgateway-cni/4.4.0/templates/daemonset.yaml new file mode 100644 index 000000000..4ba9f2669 --- /dev/null +++ b/charts/airlock/microgateway-cni/4.4.0/templates/daemonset.yaml @@ -0,0 +1,136 @@ +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: {{ include "airlock-microgateway-cni.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "airlock-microgateway-cni.labels" . | nindent 4 }} + {{- with .Values.commonAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + selector: + matchLabels: + {{- include "airlock-microgateway-cni.selectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + kubectl.kubernetes.io/default-container: cni-installer + {{- with mustMerge .Values.podAnnotations .Values.commonAnnotations}} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "airlock-microgateway-cni.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - args: + - --log-level + - "{{ .Values.config.logLevel }}" + env: + - name: CNI_NETWORK_CONFIG + valueFrom: + configMapKeyRef: + key: plugin-conf.json + name: {{ include "airlock-microgateway-cni.fullname" . }} + - name: CNI_BIN_DIR + value: /host/opt/cni/bin + - name: CNI_NET_DIR + value: /host/etc/cni/net.d + - name: KUBECONFIG_FILE_NAME + value: "{{ include "airlock-microgateway-cni.fullname" . }}-kubeconfig" + - name: INSTALL_MODE + value: {{ .Values.config.installMode }} + - name: KUBERNETES_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + image: {{ include "airlock-microgateway-cni.image" .Values.image }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + name: cni-installer + {{- with .Values.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + startupProbe: + exec: + command: + - /cni-installer + - probe + failureThreshold: 5 + initialDelaySeconds: 3 + periodSeconds: 3 + timeoutSeconds: 3 + readinessProbe: + exec: + command: + - /cni-installer + - probe + failureThreshold: 1 + periodSeconds: 60 + timeoutSeconds: 3 + securityContext: + allowPrivilegeEscalation: {{ .Values.privileged }} + capabilities: + drop: + - ALL + privileged: {{ .Values.privileged }} + readOnlyRootFilesystem: true + runAsGroup: 0 + runAsNonRoot: false + runAsUser: 0 + seccompProfile: + type: RuntimeDefault + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /host/opt/cni/bin + name: cni-bin-dir + - mountPath: /host/etc/cni/net.d + name: cni-net-dir + - mountPath: /run/cni-installer + name: cni-installer-status + hostNetwork: true + priorityClassName: system-node-critical + restartPolicy: Always + securityContext: + fsGroup: 0 + runAsGroup: 0 + runAsNonRoot: false + runAsUser: 0 + serviceAccountName: {{ include "airlock-microgateway-cni.serviceAccountName" . }} + terminationGracePeriodSeconds: 5 + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + tolerations: + - effect: NoSchedule + operator: Exists + - key: CriticalAddonsOnly + operator: Exists + - effect: NoExecute + operator: Exists + volumes: + - hostPath: + path: "{{ .Values.config.cniBinDir }}" + type: Directory + name: cni-bin-dir + - hostPath: + path: "{{ .Values.config.cniNetDir }}" + type: Directory + name: cni-net-dir + - emptyDir: {} + name: cni-installer-status diff --git a/charts/airlock/microgateway-cni/4.4.0/templates/network-attachment-definition.yaml b/charts/airlock/microgateway-cni/4.4.0/templates/network-attachment-definition.yaml new file mode 100644 index 000000000..5d657e309 --- /dev/null +++ b/charts/airlock/microgateway-cni/4.4.0/templates/network-attachment-definition.yaml @@ -0,0 +1,13 @@ +{{- if .Values.multusNetworkAttachmentDefinition.create -}} +apiVersion: "k8s.cni.cncf.io/v1" +kind: NetworkAttachmentDefinition +metadata: + name: {{ include "airlock-microgateway-cni.fullname" . }} + namespace: {{ .Values.multusNetworkAttachmentDefinition.namespace }} + labels: + {{- include "airlock-microgateway-cni.labels" . | nindent 4 }} + {{- with .Values.commonAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end -}} diff --git a/charts/airlock/microgateway-cni/4.4.0/templates/scc-role.yaml b/charts/airlock/microgateway-cni/4.4.0/templates/scc-role.yaml new file mode 100644 index 000000000..862748692 --- /dev/null +++ b/charts/airlock/microgateway-cni/4.4.0/templates/scc-role.yaml @@ -0,0 +1,22 @@ +{{- if .Values.rbac.createSCCRole -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "airlock-microgateway-cni.fullname" . }}-privileged + namespace: {{ .Release.Namespace }} + labels: + {{- include "airlock-microgateway-cni.labels" . | nindent 4 }} + {{- with .Values.commonAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +rules: +- apiGroups: + - security.openshift.io + resourceNames: + - privileged + resources: + - securitycontextconstraints + verbs: + - use +{{- end -}} \ No newline at end of file diff --git a/charts/airlock/microgateway-cni/4.4.0/templates/scc-rolebinding.yaml b/charts/airlock/microgateway-cni/4.4.0/templates/scc-rolebinding.yaml new file mode 100644 index 000000000..ebd02982c --- /dev/null +++ b/charts/airlock/microgateway-cni/4.4.0/templates/scc-rolebinding.yaml @@ -0,0 +1,20 @@ +{{- if .Values.rbac.createSCCRole -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "airlock-microgateway-cni.fullname" . }}-privileged + namespace: {{ .Release.Namespace }} + labels: + {{- include "airlock-microgateway-cni.labels" . | nindent 4 }} + {{- with .Values.commonAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "airlock-microgateway-cni.fullname" . }}-privileged +subjects: +- kind: ServiceAccount + name: {{ include "airlock-microgateway-cni.serviceAccountName" . }} +{{- end -}} diff --git a/charts/airlock/microgateway-cni/4.4.0/templates/serviceaccount.yaml b/charts/airlock/microgateway-cni/4.4.0/templates/serviceaccount.yaml new file mode 100644 index 000000000..3dc8d58ea --- /dev/null +++ b/charts/airlock/microgateway-cni/4.4.0/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "airlock-microgateway-cni.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "airlock-microgateway-cni.labels" . | nindent 4 }} + {{- with mustMerge .Values.serviceAccount.annotations .Values.commonAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end -}} diff --git a/charts/airlock/microgateway-cni/4.4.0/templates/tests/rbac.yaml b/charts/airlock/microgateway-cni/4.4.0/templates/tests/rbac.yaml new file mode 100644 index 000000000..744799333 --- /dev/null +++ b/charts/airlock/microgateway-cni/4.4.0/templates/tests/rbac.yaml @@ -0,0 +1,64 @@ +{{- if .Values.tests.enabled -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: "{{ include "airlock-microgateway-cni.fullname" . }}-tests" + namespace: {{ .Release.Namespace }} + labels: + {{- include "airlock-microgateway-cni.labelsWithoutComponent" . | nindent 4 }} + app.kubernetes.io/component: tests +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: "{{ include "airlock-microgateway-cni.fullname" . }}-tests" + namespace: {{ .Release.Namespace }} + labels: + {{- include "airlock-microgateway-cni.labelsWithoutComponent" . | nindent 4 }} + app.kubernetes.io/component: tests +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: "{{ include "airlock-microgateway-cni.fullname" . }}-tests" +subjects: +- kind: ServiceAccount + name: "{{ include "airlock-microgateway-cni.fullname" . }}-tests" +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: "{{ include "airlock-microgateway-cni.fullname" . }}-tests" + namespace: {{ .Release.Namespace }} + labels: + {{- include "airlock-microgateway-cni.labelsWithoutComponent" . | nindent 4 }} + app.kubernetes.io/component: tests +rules: +- apiGroups: + - "apps" + resources: + - daemonsets + resourceNames: + - {{ include "airlock-microgateway-cni.fullname" . }} + verbs: + - get + - watch + - list +- apiGroups: + - "" + resources: + - pods + - pods/log + verbs: + - get + - list +{{- if .Values.rbac.createSCCRole }} +- apiGroups: + - security.openshift.io + resourceNames: + - privileged + resources: + - securitycontextconstraints + verbs: + - use +{{- end -}} +{{- end -}} diff --git a/charts/airlock/microgateway-cni/4.4.0/templates/tests/test-install.yaml b/charts/airlock/microgateway-cni/4.4.0/templates/tests/test-install.yaml new file mode 100644 index 000000000..12d8c8de7 --- /dev/null +++ b/charts/airlock/microgateway-cni/4.4.0/templates/tests/test-install.yaml @@ -0,0 +1,103 @@ +{{- if .Values.tests.enabled -}} +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "airlock-microgateway-cni.fullname" . }}-test-install" + namespace: {{ .Release.Namespace }} + labels: + {{- include "airlock-microgateway-cni.labelsWithoutComponent" . | nindent 4 }} + app.kubernetes.io/component: test-install + annotations: + helm.sh/hook: test + helm.sh/hook-delete-policy: before-hook-creation +spec: + restartPolicy: Never + containers: + - name: test + image: "bitnami/kubectl:{{ .Capabilities.KubeVersion.Major }}.{{ .Capabilities.KubeVersion.Minor }}" + securityContext: + allowPrivilegeEscalation: {{ .Values.privileged }} + capabilities: + drop: + - ALL + privileged: {{ .Values.privileged }} + readOnlyRootFilesystem: true + runAsGroup: 0 + runAsNonRoot: false + runAsUser: 0 + seccompProfile: + type: RuntimeDefault + volumeMounts: + - mountPath: /host/opt/cni/bin + name: cni-bin-dir + readOnly: true + - mountPath: /host/etc/cni/net.d + name: cni-net-dir + readOnly: true + command: + - sh + - -c + - | + set -eu + + fail() { + echo "Error: ${1}" + echo "" + echo 'CNI installer logs:' + kubectl logs -n {{ .Release.Namespace }} daemonsets/{{ include "airlock-microgateway-cni.fullname" .}} -c cni-installer + exit 1 + } + + containsMGWCNIConf() { + cat "${1}" | grep -qe '"type":.*"{{ include "airlock-microgateway-cni.fullname" . }}"' + } + + if ! kubectl rollout status --timeout=60s -n {{ .Release.Namespace }} daemonsets/{{ include "airlock-microgateway-cni.fullname" .}}; then + fail 'CNI DaemonSet rollout did not complete within timeout' + fi + + echo "Checking whether CNI binary was installed" + if ! [ -f "/host/opt/cni/bin/{{ include "airlock-microgateway-cni.fullname" . }}" ]; then + fail 'CNI binary was not installed' + fi + + echo "Checking whether CNI kubeconfig was installed" + if ! [ -f "/host/etc/cni/net.d/{{ include "airlock-microgateway-cni.fullname" . }}-kubeconfig" ]; then + fail 'CNI kubeconfig was not created' + fi + + echo "Checking whether CNI configuration was written" + case {{ .Values.config.installMode }} in + "chained") + for file in "/host/etc/cni/net.d/"*.conflist; do + if containsMGWCNIConf "${file}"; then + echo "Success" + exit 0 + fi + done + ;; + "standalone") + if containsMGWCNIConf "/host/etc/cni/net.d/{{ include "airlock-microgateway-cni.fullname" . }}.conflist"; then + echo "Success" + exit 0 + fi + ;; + "manual") + echo "- Skipping because we are in 'manual' install mode" + echo "Success" + exit 0 + ;; + esac + + fail 'Configuration for plugin "{{ include "airlock-microgateway-cni.fullname" . }}" was not found' + serviceAccountName: "{{ include "airlock-microgateway-cni.fullname" . }}-tests" + volumes: + - hostPath: + path: "{{ .Values.config.cniBinDir }}" + type: Directory + name: cni-bin-dir + - hostPath: + path: "{{ .Values.config.cniNetDir }}" + type: Directory + name: cni-net-dir +{{- end -}} diff --git a/charts/airlock/microgateway-cni/4.4.0/values.schema.json b/charts/airlock/microgateway-cni/4.4.0/values.schema.json new file mode 100644 index 000000000..e087bd700 --- /dev/null +++ b/charts/airlock/microgateway-cni/4.4.0/values.schema.json @@ -0,0 +1,225 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "nameOverride": { + "type": "string" + }, + "fullnameOverride": { + "type": "string" + }, + "commonLabels": { + "$ref": "#/definitions/StringMap" + }, + "commonAnnotations": { + "$ref": "#/definitions/StringMap" + }, + "imagePullSecrets": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "name" + ], + "additionalProperties": true + } + }, + "image": { + "$ref": "#/definitions/Image" + }, + "podAnnotations": { + "$ref": "#/definitions/StringMap" + }, + "podLabels": { + "$ref": "#/definitions/StringMap" + }, + "resources": { + "type": "object" + }, + "nodeSelector": { + "$ref": "#/definitions/StringMap" + }, + "affinity": { + "type": "object" + }, + "rbac": { + "type": "object", + "properties": { + "create": { + "type": "boolean" + }, + "createSCCRole": { + "type": "boolean" + } + }, + "required": [ + "create", + "createSCCRole" + ], + "additionalProperties": false + }, + "privileged": { + "type": "boolean" + }, + "serviceAccount": { + "type": "object", + "properties": { + "create": { + "type": "boolean" + }, + "annotations": { + "$ref": "#/definitions/StringMap" + }, + "name": { + "type": "string" + } + }, + "required": [ + "annotations", + "create", + "name" + ], + "additionalProperties": false + }, + "multusNetworkAttachmentDefinition": { + "type": "object", + "properties": { + "create": { + "type": "boolean" + }, + "namespace": { + "type": "string" + } + }, + "required": [ + "create", + "namespace" + ], + "additionalProperties": false + }, + "config": { + "type": "object", + "properties": { + "installMode": { + "type": "string", + "enum": [ + "chained", + "standalone", + "manual" + ] + }, + "logLevel": { + "type": "string", + "enum": [ + "debug", + "info", + "warn", + "error" + ] + }, + "cniNetDir": { + "type": "string", + "minLength": 1 + }, + "cniBinDir": { + "type": "string", + "minLength": 1 + }, + "excludeNamespaces": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "cniBinDir", + "cniNetDir", + "excludeNamespaces", + "installMode", + "logLevel" + ], + "additionalProperties": false + }, + "tests": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + }, + "required": [ + "enabled" + ], + "additionalProperties": false + }, + "global": { + "type": "object" + } + }, + "required": [ + "affinity", + "commonAnnotations", + "commonLabels", + "config", + "fullnameOverride", + "image", + "imagePullSecrets", + "multusNetworkAttachmentDefinition", + "nameOverride", + "nodeSelector", + "podAnnotations", + "podLabels", + "privileged", + "rbac", + "resources", + "serviceAccount", + "tests" + ], + "additionalProperties": false, + "definitions": { + "StringMap": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "Image": { + "type": "object", + "properties": { + "repository": { + "type": "string", + "minLength": 1 + }, + "tag": { + "type": "string" + }, + "digest": { + "type": "string", + "pattern": "^$|^sha256:[a-f0-9]{64}$" + }, + "pullPolicy": { + "type": "string", + "enum": [ + "Always", + "IfNotPresent", + "Never" + ] + } + }, + "required": [ + "digest", + "pullPolicy", + "repository", + "tag" + ], + "additionalProperties": false + } + } +} diff --git a/charts/airlock/microgateway-cni/4.4.0/values.yaml b/charts/airlock/microgateway-cni/4.4.0/values.yaml new file mode 100644 index 000000000..1b6d8d3b6 --- /dev/null +++ b/charts/airlock/microgateway-cni/4.4.0/values.yaml @@ -0,0 +1,85 @@ +# -- Allows overriding the name to use instead of "microgateway-cni". +nameOverride: "" +# -- Allows overriding the name to use as full name of resources. +fullnameOverride: "" +# -- Labels to add to all resources. +commonLabels: {} +# -- Annotations to add to all resources. +commonAnnotations: {} +# -- ImagePullSecrets to use when pulling images. +imagePullSecrets: [] +# - name: myRegistryKeySecretName + +# Specifies the Airlock Microgateway CNI image. +image: + # -- Image repository from which to pull the Airlock Microgateway CNI image. + repository: "quay.io/airlock/microgateway-cni" + # -- Image tag to pull. + tag: "4.4.0" + # -- SHA256 image digest to pull (in the format "sha256:7144f7bab3d4c2648d7e59409f15ec52a18006a128c733fcff20d3a4a54ba44a"). + # Overrides tag when specified. + digest: "sha256:e9d711dfe75d515ad8bc5ba5e668e7a26c063bd6a291305aac458c2cbd3945f2" + # -- Pull policy for this image. + pullPolicy: IfNotPresent +# -- Annotations to add to all Pods. +podAnnotations: {} +# -- Labels to add to all Pods. +podLabels: {} +# -- Resource restrictions to apply to the CNI installer container. +resources: + requests: + cpu: 10m + memory: 100Mi +# -- NodeSelector to apply to the CNI DaemonSet in order to only deploy the CNI plugin on specific nodes. +nodeSelector: + kubernetes.io/os: linux +# -- Custom affinity for the DaemonSet to only deploy the CNI plugin on specific nodes. +affinity: {} +# Configures the generation of RBAC Roles and RoleBindings. +rbac: + # -- Whether to create RBAC resources which are required for the CNI plugin to function. + create: true + # -- (OpenShift) Whether to create RBAC resources which allow the CNI installer to use the "privileged" security context constraint. + createSCCRole: false +# -- Whether the DaemonSet should run in privileged mode. Must be enabled for environments which require it for writing files to the host (e.g. OpenShift). +privileged: false +# Configures the generation of the ServiceAccount. +serviceAccount: + # -- Whether a ServiceAccount should be created. + create: true + # -- Annotations to add to the ServiceAccount. + annotations: {} + # -- Name of the ServiceAccount to use. + # If not set and create is true, a name is generated using the fullname template. + name: "" +# Configures the generation of a NetworkAttachmentDefinition for use with Multus CNI (OpenShift) +multusNetworkAttachmentDefinition: + # -- Whether a NetworkAttachmentDefinition CR should be created, which can be used for applying the CNI plugin to Pods. + create: false + # -- Namespace in which the NetworkAttachmentDefinition is deployed. + # Note: If namespace is set to a custom value, referencing the created NetworkAttachmentDefinition from other namespaces + # may not work if Multus namespace isolation is enabled. https://github.com/k8snetworkplumbingwg/multus-cni/blob/v4.0.2/docs/configuration.md#namespace-isolation + namespace: default +# Parameters for the CNI installer configuration. +config: + # -- Whether to install the CNI plugin as a `chained` plugin (default, required with most interface CNI providers), + # as a `standalone` plugin (required for use with Multus CNI, e.g. on OpenShift) + # or in `manual` mode, where no CNI network configuration is written. + installMode: "chained" + # -- Log level for the CNI installer and plugin. + logLevel: info + # -- Directory where the CNI config files reside on the host. + # This path can either be found in the documentation of your Kubernetes distribution or CNI provider. + # It can also be queried by running the command `crictl info -o go-template --template '{{.config.cni.confDir}}'` on your Kubernetes node. + cniNetDir: "/etc/cni/net.d" + # -- Directory where the CNI plugin binaries reside on the host. + # This path can either be found in the documentation of your Kubernetes distribution or CNI provider. + # It can also be queried by running the command `crictl info -o go-template --template '{{.config.cni.binDir}}'` on your Kubernetes node. + cniBinDir: "/opt/cni/bin" + # -- Namespaces for which this CNI plugin should not apply any modifications. + excludeNamespaces: + - kube-system +tests: + # -- Whether additional resources required for running `helm test` should be created (e.g. Roles and ServiceAccounts). + # If set to false, `helm test` will not run any tests. + enabled: false diff --git a/charts/airlock/microgateway/4.4.0/.helmignore b/charts/airlock/microgateway/4.4.0/.helmignore new file mode 100644 index 000000000..101ff5ac5 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/.helmignore @@ -0,0 +1,28 @@ +# 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/ +# CRDs kustomization.yaml +/crds/kustomization.yaml +# Helm unit tests +/tests +/validation diff --git a/charts/airlock/microgateway/4.4.0/Chart.yaml b/charts/airlock/microgateway/4.4.0/Chart.yaml new file mode 100644 index 000000000..25d5570c2 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/Chart.yaml @@ -0,0 +1,44 @@ +annotations: + artifacthub.io/category: security + artifacthub.io/license: MIT + artifacthub.io/links: | + - name: Airlock Microgateway Documentation + url: https://docs.airlock.com/microgateway/4.4/ + - name: Airlock Microgateway Labs + url: https://play.instruqt.com/airlock/invite/hyi9fy4b4jzc?icp_referrer=artifacthub.io + - name: Airlock Microgateway Forum + url: https://forum.airlock.com/ + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Airlock Microgateway + catalog.cattle.io/kube-version: '>=1.25.0-0' + catalog.cattle.io/release-name: microgateway + charts.openshift.io/name: Airlock Microgateway +apiVersion: v2 +appVersion: 4.4.0 +description: A Helm chart for deploying the Airlock Microgateway +home: https://www.airlock.com/en/microgateway +icon: file://assets/icons/microgateway.svg +keywords: +- WAF +- Web Application Firewall +- WAAP +- Web Application and API protection +- OWASP +- Airlock +- Microgateway +- Security +- Filtering +- DevSecOps +- shift left +- control plane +- Operator +kubeVersion: '>=1.25.0-0' +maintainers: +- email: support@airlock.com + name: Airlock + url: https://www.airlock.com/ +name: microgateway +sources: +- https://github.com/airlock/microgateway +type: application +version: 4.4.0 diff --git a/charts/airlock/microgateway/4.4.0/README.md b/charts/airlock/microgateway/4.4.0/README.md new file mode 100644 index 000000000..b6f66cdcd --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/README.md @@ -0,0 +1,186 @@ +# Airlock Microgateway + +![Version: 4.4.0](https://img.shields.io/badge/Version-4.4.0-informational?style=flat-square) ![AppVersion: 4.4.0](https://img.shields.io/badge/AppVersion-4.4.0-informational?style=flat-square) + +*Airlock Microgateway is a Kubernetes native WAAP (Web Application and API Protection) solution to protect microservices.* + + + + + Microgateway + + +Modern application security is embedded in the development workflow and follows DevSecOps paradigms. Airlock Microgateway is the perfect fit for these requirements. It is a lightweight alternative to the Airlock Gateway appliance, optimized for Kubernetes environments. Airlock Microgateway protects your applications and microservices with the tried-and-tested Airlock security features against attacks, while also providing a high degree of scalability. +__This Helm chart is part of Airlock Microgateway. See our [GitHub repo](https://github.com/airlock/microgateway/tree/4.4.0).__ + +### Features +* Kubernetes native integration with sidecar injection and Gateway API support +* Reverse proxy functionality with request routing rules, TLS termination and remote IP extraction +* Using native Envoy HTTP filters like Lua scripting, RBAC, ext_authz, JWT authentication +* Content security filters for protecting against known attacks (OWASP Top 10) +* Access control using OpenID Connect to allow only authenticated users to access the protected services +* API security features like JSON parsing, OpenAPI specification enforcement or GraphQL schema validation + +For a list of all features, view the **[comparison of the community and premium edition](https://docs.airlock.com/microgateway/latest/#data/1675772882054.html)**. + +## Documentation and links + +Check the official documentation at **[docs.airlock.com](https://docs.airlock.com/microgateway/latest/)** or the product website at **[airlock.com/microgateway](https://www.airlock.com/en/microgateway)**. The links below point out the most interesting documentation sites when starting with Airlock Microgateway. + +* [Getting Started](https://docs.airlock.com/microgateway/latest/#data/1660804708742.html) +* [System Architecture](https://docs.airlock.com/microgateway/latest/#data/1660804709650.html) +* [Installation](https://docs.airlock.com/microgateway/latest/#data/1660804708637.html) +* [Troubleshooting](https://docs.airlock.com/microgateway/latest/#data/1659430054787.html) +* [GitHub](https://github.com/airlock/microgateway) + +# Quick start guide + +The instructions below provide a quick start guide. Detailed information are provided in the **[manual](https://docs.airlock.com/microgateway/latest/)**. + +## Prerequisites +* (Recommended) [Airlock Microgateway CNI](https://artifacthub.io/packages/helm/airlock-microgateway-cni/microgateway-cni) (Required for [data plane mode sidecar](https://docs.airlock.com/microgateway/latest/?topic=MGW-00000137)) +* [Airlock Microgateway License](#obtain-airlock-microgateway-license) +* [cert-manager](https://cert-manager.io/) +* [helm](https://helm.sh/docs/intro/install/) (>= v3.8.0) + +In order to use Airlock Microgateway you need a license and the cert-manager. You may either request a community license free of charge or purchase a premium license. +For an easy start in non-production environments, you may deploy the same cert-manager we are using internally for testing. +### Obtain Airlock Microgateway License +1. Either request a community or premium license + * Community license: [airlock.com/microgateway-community](https://airlock.com/en/microgateway-community) + * Premium license: [airlock.com/microgateway-premium](https://airlock.com/en/microgateway-premium) +2. Check your inbox and save the license file microgateway-license.txt locally. + +> See [Community vs. Premium editions in detail](https://docs.airlock.com/microgateway/latest/#data/1675772882054.html) to choose the right license type. +### Deploy cert-manager +```bash +helm repo add jetstack https://charts.jetstack.io +helm install cert-manager jetstack/cert-manager --version 'v1.16.1' -n cert-manager --create-namespace --set crds.enabled=true --wait +``` + +## Deploy Airlock Microgateway Operator + +> This guide assumes a microgateway-license.txt file is present in the working directory. + +1. Install CRDs and Operator. + ```bash + # Create namespace + kubectl create namespace airlock-microgateway-system + + # Install License + kubectl -n airlock-microgateway-system create secret generic airlock-microgateway-license --from-file=microgateway-license.txt + + # Install Operator (CRDs are included via the standard Helm 3 mechanism, i.e. Helm will handle initial installation but not upgrades) + helm install airlock-microgateway -n airlock-microgateway-system oci://quay.io/airlockcharts/microgateway --version '4.4.0' --wait + ``` + +2. (Recommended) You can verify the correctness of the installation with `helm test`. + ```bash + helm upgrade airlock-microgateway -n airlock-microgateway-system --set tests.enabled=true --reuse-values oci://quay.io/airlockcharts/microgateway --version '4.4.0' + helm test airlock-microgateway -n airlock-microgateway-system --logs + helm upgrade airlock-microgateway -n airlock-microgateway-system --set tests.enabled=false --reuse-values oci://quay.io/airlockcharts/microgateway --version '4.4.0' + ``` + +### Upgrading CRDs + +The `helm install/upgrade` command currently does not support upgrading CRDs that already exist in the cluster. +CRDs should instead be manually upgraded before upgrading the Operator itself via the following command: +```bash +kubectl apply -k https://github.com/airlock/microgateway/deploy/charts/airlock-microgateway/crds/?ref=4.4.0 --server-side --force-conflicts +``` + +**Note**: Certain GitOps solutions such as e.g. Argo CD or Flux CD have their own mechanisms for automatically upgrading CRDs included with Helm charts. + +## Support + +### Premium support +If you have a paid license, please follow the [premium support process](https://techzone.ergon.ch/support-process). + +### Community support +For the community edition, check our **[Airlock community forum](https://forum.airlock.com/)** for FAQs or register to post your question. +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| commonAnnotations | object | `{}` | Annotations to add to all resources. | +| commonLabels | object | `{}` | Labels to add to all resources. | +| crds.skipVersionCheck | bool | `false` | Whether to skip the sanity check which prevents installing/upgrading the helm chart in a cluster with outdated Airlock Microgateway CRDs. The check aims to prevent unexpected behavior and issues due to Helm v3 not automatically upgrading CRDs which are already present in the cluster when performing a "helm install/upgrade". | +| dashboards.config.grafana.dashboardLabel.name | string | `"grafana_dashboard"` | Name of the label that lets Grafana identify ConfigMaps that represent dashboards. | +| dashboards.config.grafana.dashboardLabel.value | string | `"1"` | Value of the label that lets Grafana identify ConfigMaps that represent dashboards. | +| dashboards.config.grafana.folderAnnotation.name | string | `"grafana_folder"` | Name of the annotation containing the folder name to file dashboards into. | +| dashboards.config.grafana.folderAnnotation.value | string | `"Airlock Microgateway"` | Name of the folder dashboards are filed into within the Grafana UI. | +| dashboards.create | bool | `false` | Whether to create any ConfigMaps containing Grafana dashboards to import. | +| dashboards.instances.blockLogs.create | bool | `true` | Whether to create the block logs dashboard. | +| dashboards.instances.blockMetrics.create | bool | `true` | Whether to create the block metrics dashboard. | +| dashboards.instances.headerLogs.create | bool | `true` | Whether to create the header rewrite logs dashboard. | +| dashboards.instances.license.create | bool | `true` | Whether to create the license dashboard. | +| dashboards.instances.logOnlyLogs.create | bool | `true` | Whether to create the log only logs dashboard. | +| dashboards.instances.logOnlyMetrics.create | bool | `true` | Whether to create the log only metrics dashboard | +| dashboards.instances.overview.create | bool | `true` | Whether to create the overview dashboard. | +| engine.image.digest | string | `"sha256:c29adf07e7536b72447ea694d0e19fe19235306c26d412a9abc43e4dd99b84c8"` | SHA256 image digest to pull (in the format "sha256:a3051f42d3013813b05f7513bb86ed6a3209cb3003f1bb2f7b72df249aa544d3"). Overrides tag when specified. | +| engine.image.pullPolicy | string | `"IfNotPresent"` | Pull policy for this image. | +| engine.image.repository | string | `"quay.io/airlock/microgateway-engine"` | Image repository from which to pull the Airlock Microgateway Engine image. | +| engine.image.tag | string | `"4.4.0"` | Image tag to pull. | +| engine.resources | object | `{}` | Resource restrictions to apply to the Airlock Microgateway Engine container. | +| engine.sidecar.podMonitor.create | bool | `false` | Whether to create a PodMonitor resource for monitoring. | +| engine.sidecar.podMonitor.labels | object | `{}` | Labels to add to the PodMonitor. | +| fullnameOverride | string | `""` | Allows overriding the name to use as full name of resources. | +| imagePullSecrets | list | `[]` | ImagePullSecrets to use when pulling images. | +| license.secretName | string | `"airlock-microgateway-license"` | Name of the secret containing the "microgateway-license.txt" key. | +| nameOverride | string | `""` | Allows overriding the name to use instead of "microgateway". | +| networkValidator.image.digest | string | `"sha256:05585644690678ae6453ab12e3a5f899e7be5ab70f56c6bf1c4484d3b53587d2"` | SHA256 image digest to pull (in the format "sha256:05585644690678ae6453ab12e3a5f899e7be5ab70f56c6bf1c4484d3b53587d2"). Overrides tag when specified. | +| networkValidator.image.pullPolicy | string | `"IfNotPresent"` | Pull policy for this image. | +| networkValidator.image.repository | string | `"cgr.dev/chainguard/netcat"` | Image repository from which to pull the netcat image for the Airlock Microgateway Network Validator init-container. | +| networkValidator.image.tag | string | `""` | Image tag to pull. | +| networkValidator.resources | object | `{"limits":{"cpu":"25m","memory":"12Mi"},"requests":{"cpu":"5m","memory":"1Mi"}}` | Resource restrictions to apply to the Airlock Microgateway Network Validator init-container. | +| operator.affinity | object | `{}` | Custom affinity to apply to the operator Deployment. Used to influence the scheduling. | +| operator.config.logLevel | string | `"info"` | Operator application log level. | +| operator.gatewayAPI.controllerName | string | `"microgateway.airlock.com/gatewayclass-controller"` | Controller name referred in the GatewayClasses managed by this operator. The value must be a path prefixed by the domain `microgateway.airlock.com`. | +| operator.gatewayAPI.enabled | bool | `false` | Whether to enable the Kubernetes Gateway API related controllers. Requires that the gateway.networking.k8s.io/v1 resources are installed on the cluster. | +| operator.image.digest | string | `"sha256:80cbae58ad9badd9395fa09a7b0576561821121b8353146bbd6efa2240ab5d97"` | SHA256 image digest to pull (in the format "sha256:c79ee3f85862fb386e9dd62b901b607161d27807f512d7fbdece05e9ee3d7c63"). Overrides tag when specified. | +| operator.image.pullPolicy | string | `"IfNotPresent"` | Pull policy for this image. | +| operator.image.repository | string | `"quay.io/airlock/microgateway-operator"` | Image repository from which to pull the Airlock Microgateway Operator image. | +| operator.image.tag | string | `"4.4.0"` | Image tag to pull. | +| operator.nodeSelector | object | `{}` | Custom nodeSelector to apply to the operator Deployment in order to constrain its Pods to certain nodes. | +| operator.podAnnotations | object | `{}` | Annotations to add to all Pods. | +| operator.podLabels | object | `{}` | Labels to add to all Pods. | +| operator.rbac.create | bool | `true` | Whether to create RBAC resources which are required for the Airlock Microgateway Operator to function. | +| operator.replicaCount | int | `2` | Number of replicas for the operator Deployment. | +| operator.resources | object | `{}` | Resource restrictions to apply to the operator container. | +| operator.serviceAccount.annotations | object | `{}` | Annotations to add to the ServiceAccount. | +| operator.serviceAccount.create | bool | `true` | Whether a ServiceAccount should be created. | +| operator.serviceAccount.name | string | `""` | Name of the ServiceAccount to use. If not set and create is true, a name is generated using the fullname template. | +| operator.serviceAnnotations | object | `{}` | Annotations to add to the Service. | +| operator.serviceLabels | object | `{}` | Labels to add to the Service. | +| operator.serviceMonitor.create | bool | `false` | Whether to create a ServiceMonitor resource for monitoring. | +| operator.serviceMonitor.labels | object | `{}` | Labels to add to the ServiceMonitor. | +| operator.tolerations | list | `[]` | Custom tolerations to apply to the operator Deployment in order to allow its Pods to run on tainted nodes. | +| operator.updateStrategy | object | `{"type":"RollingUpdate"}` | Specifies the operator update strategy. | +| operator.watchNamespaceSelector | object | `{}` | Allows to dynamically select watch namespaces of the operator and the scope of the webhooks based on a Namespace label selector. It is able to detect and reconcile resources in all namespaces that match the label selector automatically, even for new namespaces, without restarting the operator. This facilitates a dynamic `MultiNamespace` installation mode, but still requires cluster-scoped permissions (i.e., ClusterRoles and ClusterRoleBindings). An `AllNamespaces` installation or the usage of the `watchNamespaces` requires the `watchNamespaceSelector` to be empty. Please note that this feature requires a Premium license. | +| operator.watchNamespaces | list | `[]` | Allows to restrict the operator to specific namespaces, depending on your needs. For a `OwnNamespace` or `SingleNamespace` installation the list may only contain one namespace (e.g., `watchNamespaces: ["airlock-microgateway-system"]`). In case of the `OwnNamespace` installation mode the specified namespace should be equal to the installation namespace. For a static `MultiNamespace` installation, the complete list of namespaces must be provided in the `watchNamespaces`. An `AllNamespaces` installation or the usage of the `watchNamespaceSelector` requires the `watchNamespaces` to be empty. Regardless of the installation modes supported by `watchNamespaces`, RBAC is created only namespace-scoped (using Roles and RoleBindings) in the respective namespaces. Please note that this feature requires a Premium license. | +| sessionAgent.image.digest | string | `"sha256:fbb90f2a52bb1b19cca6c5c133e80331153c019ec905db052c250fedbb09c3bc"` | SHA256 image digest to pull (in the format "sha256:a3051f42d3013813b05f7513bb86ed6a3209cb3003f1bb2f7b72df249aa544d3"). Overrides tag when specified. | +| sessionAgent.image.pullPolicy | string | `"IfNotPresent"` | Pull policy for this image. | +| sessionAgent.image.repository | string | `"quay.io/airlock/microgateway-session-agent"` | Image repository from which to pull the Airlock Microgateway Session Agent image. | +| sessionAgent.image.tag | string | `"4.4.0"` | Image tag to pull. | +| sessionAgent.resources | object | `{}` | Resource restrictions to apply to the Airlock Microgateway Session Agent container. | +| tests.enabled | bool | `false` | Whether additional resources required for running `helm test` should be created (e.g. Roles and ServiceAccounts). If set to false, `helm test` will not run any tests. | + +## License +View the [detailed license terms](https://www.airlock.com/en/airlock-license) for the software contained in this image. +* Decompiling or reverse engineering is not permitted. +* Using any of the deny rules or parts of these filter patterns outside of the image is not permitted. + +Airlock® is a security innovation by [ergon](https://www.ergon.ch/en) + + + + + + + Airlock Secure Access Hub + + diff --git a/charts/airlock/microgateway/4.4.0/app-readme.md b/charts/airlock/microgateway/4.4.0/app-readme.md new file mode 100644 index 000000000..e32cac025 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/app-readme.md @@ -0,0 +1,28 @@ +# Airlock Microgateway + +*Airlock Microgateway is a Kubernetes native WAAP (Web Application and API Protection) solution to protect microservices.* + +## Features +* Kubernetes native integration with its Operator, Custom Resource Definitions, hot-reload, automatic sidecar injection. +* Reverse proxy functionality with request routing rules, TLS termination and remote IP extraction +* Using native Envoy HTTP filters like Lua scripting, RBAC, ext_authz, JWT authentication +* Content security filters for protecting against known attacks (OWASP Top 10) +* Access control to allow only authenticated users to access the protected services +* API security features like JSON parsing or OpenAPI specification enforcement + +For a list of all features, view the **[comparison of the community and premium edition](https://docs.airlock.com/microgateway/latest/#data/1675772882054.html)**. + +## Requirements +* [Airlock Microgateway CNI Helm Chart](https://artifacthub.io/packages/helm/airlock-microgateway-cni/microgateway-cni) (Also available as Rancher Chart) +* [Airlock Microgateway License](https://github.com/airlock/microgateway?tab=readme-ov-file#obtain-airlock-microgateway-license) (After obtaining the license install it according to the [documentation](https://github.com/airlock/microgateway?tab=readme-ov-file#deploy-airlock-microgateway-operator)) +* [cert-manager](https://cert-manager.io/docs/installation/) + +## Documentation and links + +Check the official documentation at **[docs.airlock.com](https://docs.airlock.com/microgateway/latest/)** or the product website at **[airlock.com/microgateway](https://www.airlock.com/en/microgateway)**. The links below point out the most interesting documentation sites when starting with Airlock Microgateway. + +* [Getting Started](https://docs.airlock.com/microgateway/latest/#data/1660804708742.html) +* [System Architecture](https://docs.airlock.com/microgateway/latest/#data/1660804709650.html) +* [Installation](https://docs.airlock.com/microgateway/latest/#data/1660804708637.html) +* [Troubleshooting](https://docs.airlock.com/microgateway/latest/#data/1659430054787.html) +* [GitHub](https://github.com/airlock/microgateway) \ No newline at end of file diff --git a/charts/airlock/microgateway/4.4.0/crds/accesscontrols.microgateway.airlock.com.yaml b/charts/airlock/microgateway/4.4.0/crds/accesscontrols.microgateway.airlock.com.yaml new file mode 100644 index 000000000..9e6c7047d --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/crds/accesscontrols.microgateway.airlock.com.yaml @@ -0,0 +1,501 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + labels: + app.kubernetes.io/name: airlock-microgateway-operator + app.kubernetes.io/version: 4.4.0 + name: accesscontrols.microgateway.airlock.com +spec: + group: microgateway.airlock.com + names: + categories: + - airlock-microgateway + kind: AccessControl + listKind: AccessControlList + plural: accesscontrols + singular: accesscontrol + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: AccessControl specifies the options to perform access control with a Microgateway Engine container. + 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: Specifies how the Airlock Microgateway Engine performs access control. + properties: + policies: + description: Policies configures access control policies. The first matching policy (from top to bottom) applies. + items: + properties: + authorization: + description: Authorization configures how requests are authorized. An empty object value {} disables authorization. + properties: + authentication: + description: Authentication specifies that clients need to be authenticated with the provided method. + properties: + oidc: + description: OIDC configures client authentication using OpenID Connect. + properties: + oidcRelyingPartyRef: + description: OIDCRelyingPartyRef configures how the Airlock Microgateway Engine interacts with the OpenID provider. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + required: + - oidcRelyingPartyRef + type: object + type: object + deny: + description: Deny specifies to deny access for all requests matching this policy. + type: object + requireAll: + description: RequireAll specifies conditions which must all be satisfied for the request to be authorized. + items: + properties: + oidc: + description: OIDC specifies a condition on the result of an OpenID Connect flow. + properties: + claim: + description: Claim specifies a condition on a JWT claim. + properties: + name: + description: Name of the claim. + minLength: 1 + type: string + value: + description: |- + Value of the claim. If not specified, only existence of the claim is checked (any value is allowed). + + Value matching is only supported if the data type of the claim is either primitive (`number`, `boolean`, `string`) or `array` of primitives. + In case of a non-string value, the match will be performed against the stringified value. + + If the claim has an unsupported data type (e.g. `object` or `null`), its value will never match. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + required: + - name + type: object + required: + - claim + type: object + required: + - oidc + type: object + minItems: 1 + type: array + requireAny: + description: RequireAny specifies conditions of which at least one must be satisfied for the request to be authorized. + items: + properties: + oidc: + description: OIDC specifies a condition on the result of an OpenID Connect flow. + properties: + claim: + description: Claim specifies a condition on a JWT claim. + properties: + name: + description: Name of the claim. + minLength: 1 + type: string + value: + description: |- + Value of the claim. If not specified, only existence of the claim is checked (any value is allowed). + + Value matching is only supported if the data type of the claim is either primitive (`number`, `boolean`, `string`) or `array` of primitives. + In case of a non-string value, the match will be performed against the stringified value. + + If the claim has an unsupported data type (e.g. `object` or `null`), its value will never match. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + required: + - name + type: object + required: + - claim + type: object + required: + - oidc + type: object + minItems: 1 + type: array + type: object + identityPropagation: + description: IdentityPropagation configures how the authenticated user's identity is communicated to the protected application. + properties: + actions: + description: Actions specifies the propagation actions. + items: + properties: + identityPropagationRef: + description: IdentityPropagationRef selects an IdentityPropagation to apply. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + required: + - identityPropagationRef + type: object + type: array + onFailure: + description: |- + OnFailure configures what should happen, if an identity propagation fails. Meaning of the possible values: + _Pass_: The request should be forwarded to the upstream, without including the information from the failed identity propagations. + enum: + - Pass + type: string + required: + - actions + - onFailure + type: object + requestConditions: + description: |- + RequestConditions defines additional request properties which must be matched in order for this policy to apply. A policy without request conditions will always match. + + WARNING: There is currently a limitation that if `authentication.oidc` is configured for this policy, you must ensure that the request condition also matches logout requests and callback redirects from the OIDC Provider as configured in the OIDCRelyingParty (`pathMapping.logoutPath` / `pathMapping.redirectPath`). + properties: + header: + description: Header defines the matching headers of a request. + properties: + name: + description: Name defines the name of a header. + properties: + matcher: + description: Matcher defines the way to match a string. In comparison to a normal StringMatcher, a value is always matched ignoring the case and can't be inverted. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + value: + description: Value defines the value of a header. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + type: object + invert: + default: false + description: Invert indicates whether the request condition should be inverted. + type: boolean + mediaType: + description: MediaType defines the matching media type from the content-type header of a request. + properties: + matcher: + description: |- + NonInvertableCaseInsensitiveStringMatcher defines the way to match a string. + In comparison to a normal StringMatcher, a value is always matched ignoring the case and can't be inverted. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + method: + description: Method defines the matching methods of a request. + items: + description: Method defines common HTTP methods. + enum: + - GET + - HEAD + - POST + - PUT + - PATCH + - DELETE + - CONNECT + - OPTIONS + - TRACE + type: string + type: array + path: + description: Path defines the matching path of a request. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + remoteIP: + description: RemoteIP defines the matching remote IPs of a request. + properties: + cidrRanges: + description: CIDRRanges defines the IPv4 or IPv6 CIDR ranges, e.g. ``196.148.3.128/26`` or ``2001:db8::/28``. + items: + description: CIDRRange defines an IPv4 or IPv6 CIDR range, e.g. “196.148.3.128/26“ or “2001:db8::/28“. + format: cidr + type: string + minItems: 1 + type: array + invert: + default: false + description: Invert indicates whether the match should be inverted. + type: boolean + required: + - cidrRanges + type: object + type: object + required: + - authorization + type: object + minItems: 1 + type: array + required: + - policies + type: object + required: + - spec + type: object + served: true + storage: true + subresources: {} diff --git a/charts/airlock/microgateway/4.4.0/crds/contentsecurities.microgateway.airlock.com.yaml b/charts/airlock/microgateway/4.4.0/crds/contentsecurities.microgateway.airlock.com.yaml new file mode 100644 index 000000000..7fa610819 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/crds/contentsecurities.microgateway.airlock.com.yaml @@ -0,0 +1,139 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + labels: + app.kubernetes.io/name: airlock-microgateway-operator + app.kubernetes.io/version: 4.4.0 + name: contentsecurities.microgateway.airlock.com +spec: + group: microgateway.airlock.com + names: + categories: + - airlock-microgateway + kind: ContentSecurity + listKind: ContentSecurityList + plural: contentsecurities + singular: contentsecurity + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: ContentSecurity specifies the options to secure an upstream web application with a Microgateway Engine container. + 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: Specifies the options to secure an upstream web application with a Microgateway Engine container. + properties: + apiProtection: + description: |- + APIProtection defines the relevant configurations to protect APIs. + If undefined, default settings are applied, designed to work with most upstream web application services. + properties: + graphQLRef: + description: |- + GraphQLRef selects the relevant GraphQL configuration resource. + If undefined, default settings are applied, designed to work with most upstream web application services. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + openAPIRef: + description: |- + OpenAPIRef selects the relevant OpenAPI configuration resource. + If undefined, default settings are applied, designed to work with most upstream web application services. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + type: object + filter: + description: |- + Filter defines the set of filters, e.g. Airlock Deny Rules, to be applied to incoming requests + to protect against various attack patterns. + If undefined, default settings are applied, designed to work with most upstream web application services. + properties: + denyRulesRef: + description: |- + DenyRulesRef selects the relevant DenyRules configuration resource. + If undefined, default settings are applied, designed to work with most upstream web application services. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + type: object + headerRewritesRef: + description: |- + HeaderRewritesRef selects the relevant HeaderRewrites. + If undefined, default settings are applied, designed to work with most upstream web application services. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + limitsRef: + description: |- + LimitsRef selects the relevant Limits configuration resource. + If undefined, default settings are applied, designed to work with most upstream web application services. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + parserRef: + description: |- + ParserRef selects the relevant Parser configuration resource. + If undefined, default settings are applied, designed to work with most upstream web application services. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + type: object + type: object + served: true + storage: true + subresources: {} diff --git a/charts/airlock/microgateway/4.4.0/crds/contentsecuritypolicies.microgateway.airlock.com.yaml b/charts/airlock/microgateway/4.4.0/crds/contentsecuritypolicies.microgateway.airlock.com.yaml new file mode 100644 index 000000000..be0e8cfb8 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/crds/contentsecuritypolicies.microgateway.airlock.com.yaml @@ -0,0 +1,476 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + labels: + app.kubernetes.io/name: airlock-microgateway-operator + app.kubernetes.io/version: 4.4.0 + gateway.networking.k8s.io/policy: direct + name: contentsecuritypolicies.microgateway.airlock.com +spec: + group: microgateway.airlock.com + names: + categories: + - airlock-microgateway + kind: ContentSecurityPolicy + listKind: ContentSecurityPolicyList + plural: contentsecuritypolicies + singular: contentsecuritypolicy + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: ContentSecurityPolicy is a Direct Attached Policy for the Kubernetes Gateway API. It specifies the options to secure an upstream web application with a Microgateway. + 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 ContentSecurityPolicy. + properties: + secured: + description: Secured enables WAF processing for the routes attached to this policy. + properties: + apiProtection: + description: |- + APIProtection defines the relevant configurations to protect APIs. + If undefined, default settings are applied, designed to work with most upstream web application services. + properties: + graphQLRef: + description: |- + GraphQLRef selects the relevant GraphQL configuration resource. + If undefined, default settings are applied, designed to work with most upstream web application services. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + openAPIRef: + description: |- + OpenAPIRef selects the relevant OpenAPI configuration resource. + If undefined, default settings are applied, designed to work with most upstream web application services. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + type: object + filter: + description: |- + Filter defines the set of filters, e.g. Airlock Deny Rules, to be applied to incoming requests + to protect against various attack patterns. + If undefined, default settings are applied, designed to work with most upstream web application services. + properties: + denyRulesRef: + description: |- + DenyRulesRef selects the relevant DenyRules configuration resource. + If undefined, default settings are applied, designed to work with most upstream web application services. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + type: object + limitsRef: + description: |- + LimitsRef selects the relevant Limits configuration resource. + If undefined, default settings are applied, designed to work with most upstream web application services. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + parserRef: + description: |- + ParserRef selects the relevant Parser configuration resource. + If undefined, default settings are applied, designed to work with most upstream web application services. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + type: object + targetRefs: + description: |- + TargetRefs are the resources this policy is being attached to. Referenced resources must be in the same namespace as the policy. + Support: HTTPRoute. + items: + description: |- + LocalPolicyTargetReference identifies an API object to apply a direct or + inherited policy to. This should be used as part of Policy resources + that can target Gateway API resources. For more information on how this + policy attachment model works, and a sample Policy resource, refer to + the policy attachment documentation for Gateway API. + properties: + group: + description: Group is the group of the target resource. + 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 target resource. + 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 target resource. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + maxItems: 16 + minItems: 1 + type: array + x-kubernetes-validations: + - message: 'TargetRef Kind must be: HTTPRoute' + rule: self.all(t, t.kind=='HTTPRoute') + - message: TargetRef Group must be gateway.networking.k8s.io. + rule: self.all(t, t.group=='gateway.networking.k8s.io') + unsecured: + description: |- + Unsecured disables all WAF functionality and therefore protection for the routes attached to this policy. + WARNING: Using this setting when the application is exposed to untrusted downstream traffic is highly discouraged. + type: object + required: + - targetRefs + type: object + status: + description: Status defines the state of the ContentSecurityPolicy. + properties: + ancestors: + description: |- + Ancestors is a list of ancestor resources (usually Gateways) that are + associated with the policy, and the status of the policy with respect to + each ancestor. When this policy attaches to a parent, the controller that + manages the parent and the ancestors MUST add an entry to this list when + the controller first sees the policy and SHOULD update the entry as + appropriate when the relevant ancestor is modified. + + Note that choosing the relevant ancestor is left to the Policy designers; + an important part of Policy design is designing the right object level at + which to namespace this status. + + Note also that implementations MUST ONLY populate ancestor status for + the Ancestor resources they are responsible for. Implementations MUST + use the ControllerName field to uniquely identify the entries in this list + that they are responsible for. + + Note that to achieve this, the list of PolicyAncestorStatus structs + MUST be treated as a map with a composite key, made up of the AncestorRef + and ControllerName fields combined. + + A maximum of 16 ancestors will be represented in this list. An empty list + means the Policy is not relevant for any ancestors. + + If this slice is full, implementations MUST NOT add further entries. + Instead they MUST consider the policy unimplementable and signal that + on any related resources such as the ancestor that would be referenced + here. For example, if this list was full on BackendTLSPolicy, no + additional Gateways would be able to reference the Service targeted by + the BackendTLSPolicy. + items: + description: |- + PolicyAncestorStatus describes the status of a route with respect to an + associated Ancestor. + + Ancestors refer to objects that are either the Target of a policy or above it + in terms of object hierarchy. For example, if a policy targets a Service, the + Policy's Ancestors are, in order, the Service, the HTTPRoute, the Gateway, and + the GatewayClass. Almost always, in this hierarchy, the Gateway will be the most + useful object to place Policy status on, so we recommend that implementations + SHOULD use Gateway as the PolicyAncestorStatus object unless the designers + have a _very_ good reason otherwise. + + In the context of policy attachment, the Ancestor is used to distinguish which + resource results in a distinct application of this policy. For example, if a policy + targets a Service, it may have a distinct result per attached Gateway. + + Policies targeting the same resource may have different effects depending on the + ancestors of those resources. For example, different Gateways targeting the same + Service may have different capabilities, especially if they have different underlying + implementations. + + For example, in BackendTLSPolicy, the Policy attaches to a Service that is + used as a backend in a HTTPRoute that is itself attached to a Gateway. + In this case, the relevant object for status is the Gateway, and that is the + ancestor object referred to in this status. + + Note that a parent is also an ancestor, so for objects where the parent is the + relevant object for status, this struct SHOULD still be used. + + This struct is intended to be used in a slice that's effectively a map, + with a composite key made up of the AncestorRef and the ControllerName. + properties: + ancestorRef: + description: |- + AncestorRef corresponds with a ParentRef in the spec that this + PolicyAncestorStatus 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. + + + ParentRefs from a Route to a Service in the same namespace are "producer" + routes, which apply default routing rules to inbound connections from + any namespace to the Service. + + ParentRefs from a Route to a Service in a different namespace are + "consumer" routes, and these routing rules are only applied to outbound + connections originating from the same namespace as the Route, for which + the intended destination of the connections are a Service targeted as a + ParentRef of the Route. + + + 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. + + + When the parent resource is a Service, this targets a specific port in the + Service spec. When both Port (experimental) and SectionName are specified, + the name and port of the selected port 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 + conditions: + description: Conditions describes the status of the Policy with respect to the given Ancestor. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + 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. + 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 + required: + - ancestorRef + - controllerName + type: object + maxItems: 16 + type: array + required: + - ancestors + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/charts/airlock/microgateway/4.4.0/crds/denyrules.microgateway.airlock.com.yaml b/charts/airlock/microgateway/4.4.0/crds/denyrules.microgateway.airlock.com.yaml new file mode 100644 index 000000000..ac120c80a --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/crds/denyrules.microgateway.airlock.com.yaml @@ -0,0 +1,1812 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + labels: + app.kubernetes.io/name: airlock-microgateway-operator + app.kubernetes.io/version: 4.4.0 + name: denyrules.microgateway.airlock.com +spec: + group: microgateway.airlock.com + names: + categories: + - airlock-microgateway + kind: DenyRules + listKind: DenyRulesList + plural: denyrules + singular: denyrules + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + DenyRules configures request filtering using Airlock built-in and custom deny rules. + Deny rules establish a negative security model. They define prohibited patterns which, when a match is found in a request, lead to it being blocked from reaching the upstream web application. + To handle possible false positives, lower the security level or define fine-granular deny rule exceptions + If undefined, default settings are applied, designed to work with most upstream web application services. + 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: Specification of the desired deny rules behavior. + properties: + request: + description: Request configures deny rules for downstream requests. + properties: + builtIn: + description: BuiltIn configures the built-in deny rules. + properties: + exceptions: + description: Exceptions allows to define exceptions for specific requests and deny rules. + items: + description: |- + DenyRulesException defines an exception for deny rules. Exceptions may be defined by any or a combination of the following elements: blockedData (the request data causing a block) or requestConditions (properties of a request without taking into consideration the reason why a request has been blocked). + At least one of blockedData and requestConditions must be set. + properties: + blockedData: + description: BlockedData defines an exception based on the request data causing the block. + properties: + graphQL: + description: |- + GraphQL defines an exception based on a blocked GraphQL query. + Only one of parameter, header, path, pathSegment, json or graphQL can be set. + properties: + argument: + description: |- + Argument defines an argument of a field of the GraphQL query. + At least one of field, argument and value must be set. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + field: + description: |- + Field defines a field of the GraphQL query. + At least one of field, argument and value must be set. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + value: + description: |- + Value defines the value of an argument of the GraphQL query. + At least one of field, argument and value must be set. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + type: object + header: + description: |- + Header defines an exception based on a blocked header. + Only one of parameter, header, path, pathSegment, json or graphQL can be set. + properties: + name: + description: Name defines the name of a header. + properties: + matcher: + description: Matcher defines the way to match a string. In comparison to a normal StringMatcher, a value is always matched ignoring the case and can't be inverted. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + value: + description: Value defines the value of a header. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + type: object + json: + description: |- + JSON defines an exception based on a blocked JSON property. + Only one of parameter, header, path, pathSegment, json or graphQL can be set. + properties: + jsonPath: + description: |- + JSONPath defines the JSONPath pattern to match the path within the JSON. + Expressions in JSONPath i.e. `?(expr)` are not supported. + minLength: 1 + type: string + key: + description: |- + Key defines the key of the JSON property. + At most one of key and value can be set. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + value: + description: |- + Value defines the value of the JSON property. + At most one of key and value can be set. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + type: object + parameter: + description: |- + Parameter defines an exception based on a blocked parameter. + Only one of parameter, header, path, pathSegment, json or graphQL can be set. + properties: + name: + description: Name defines the name of a parameter. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + source: + default: Any + description: Source defines the source of the parameter. + enum: + - Query + - Post + - Any + type: string + value: + description: Value defines the value of a parameter. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + type: object + path: + description: |- + Path defines an exception based on the blocked path. + Only one of parameter, header, path, pathSegment, json or graphQL can be set. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + pathSegment: + description: |- + PathSegment defines an exception based on a blocked path segment. + Only one of parameter, header, path, pathSegment, json or graphQL can be set. + properties: + segments: + description: Segments defines the position of a segment within the path. + properties: + index: + description: Index specifies an exact path segment position by index (0-based). + minimum: 0 + type: integer + type: object + value: + description: Value defines the value of a path segment. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + type: object + type: object + requestConditions: + description: RequestConditions defines an exception based on a property of a request without taking into consideration the reason why a request has been blocked. + properties: + header: + description: Header defines the matching headers of a request. + properties: + name: + description: Name defines the name of a header. + properties: + matcher: + description: Matcher defines the way to match a string. In comparison to a normal StringMatcher, a value is always matched ignoring the case and can't be inverted. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + value: + description: Value defines the value of a header. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + type: object + invert: + default: false + description: Invert indicates whether the request condition should be inverted. + type: boolean + mediaType: + description: MediaType defines the matching media type from the content-type header of a request. + properties: + matcher: + description: |- + NonInvertableCaseInsensitiveStringMatcher defines the way to match a string. + In comparison to a normal StringMatcher, a value is always matched ignoring the case and can't be inverted. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + method: + description: Method defines the matching methods of a request. + items: + description: Method defines common HTTP methods. + enum: + - GET + - HEAD + - POST + - PUT + - PATCH + - DELETE + - CONNECT + - OPTIONS + - TRACE + type: string + type: array + path: + description: Path defines the matching path of a request. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + remoteIP: + description: RemoteIP defines the matching remote IPs of a request. + properties: + cidrRanges: + description: CIDRRanges defines the IPv4 or IPv6 CIDR ranges, e.g. ``196.148.3.128/26`` or ``2001:db8::/28``. + items: + description: CIDRRange defines an IPv4 or IPv6 CIDR range, e.g. “196.148.3.128/26“ or “2001:db8::/28“. + format: cidr + type: string + minItems: 1 + type: array + invert: + default: false + description: Invert indicates whether the match should be inverted. + type: boolean + required: + - cidrRanges + type: object + type: object + ruleKeys: + description: RuleKeys restricts the exception to a set of deny rules. + items: + description: |- + A deny rule name can be any of the following values: + ENCODING | + EXPLOIT | + HPP | + HTML | + IDOR | + LDAP | + NOSQL | + OGNL | + PHP | + PROTOCOL | + SANITY | + SCANNING | + SQL | + TEMPLATE | + UNIXCMD | + WINCMD | + XSS | + SSRF | + BOT + enum: + - ENCODING + - EXPLOIT + - HPP + - HTML + - IDOR + - LDAP + - NOSQL + - OGNL + - PHP + - PROTOCOL + - SANITY + - SCANNING + - SQL + - TEMPLATE + - UNIXCMD + - WINCMD + - XSS + - SSRF + - BOT + type: string + minItems: 1 + type: array + type: object + type: array + overrides: + description: Overrides allows to override the builtIn settings for specific deny rules. + items: + description: DenyRulesOverride allows to override the builtIn settings for specific deny rules. + properties: + conditions: + description: Conditions select which built-in deny rules' settings will be adjusted. + properties: + ruleKeys: + description: RuleKeys is a list of built-in deny rule names. + items: + description: |- + A deny rule name can be any of the following values: + ENCODING | + EXPLOIT | + HPP | + HTML | + IDOR | + LDAP | + NOSQL | + OGNL | + PHP | + PROTOCOL | + SANITY | + SCANNING | + SQL | + TEMPLATE | + UNIXCMD | + WINCMD | + XSS | + SSRF | + BOT + enum: + - ENCODING + - EXPLOIT + - HPP + - HTML + - IDOR + - LDAP + - NOSQL + - OGNL + - PHP + - PROTOCOL + - SANITY + - SCANNING + - SQL + - TEMPLATE + - UNIXCMD + - WINCMD + - XSS + - SSRF + - BOT + type: string + minItems: 1 + type: array + types: + description: Types defines the type of attributes the override should be applied on. If Types are defined without any RuleKeys the override is applied to all deny rules. + items: + description: |- + A deny rule override type name can be any of the following values: + Header | + Parameter | + Path | + JSON | + GraphQL + enum: + - Header + - Parameter + - Path + - PathSegment + - JSON + - GraphQL + type: string + minItems: 0 + type: array + type: object + settings: + description: Settings override the corresponding properties for the selected rules. + properties: + level: + description: Level specifies the filter strength. + enum: + - Unfiltered + - Basic + - Standard + - Strict + type: string + threatHandlingMode: + description: ThreatHandlingMode specifies how threats should be handled. + enum: + - Block + - LogOnly + type: string + type: object + type: object + type: array + settings: + description: Settings contains the keys which will be adjusted. + properties: + level: + default: Standard + description: Level represents a set of deny rules with different filter strengths. + enum: + - Unfiltered + - Basic + - Standard + - Strict + type: string + threatHandlingMode: + default: Block + description: ThreatHandlingMode specifies how threats should be handled when a deny rule matches. + enum: + - Block + - LogOnly + type: string + type: object + type: object + custom: + description: Custom allows configuring additional deny rules. + properties: + rules: + description: Rules defines list of additional deny rules. + items: + properties: + blockData: + description: BlockData specifies the request data which should cause a block. + properties: + graphQL: + description: |- + GraphQL specifies to block requests containing a matching GraphQL property. + At least one of field, argument and value must be set. + properties: + argument: + description: |- + Argument defines an argument of a field of the GraphQL query. + At least one of field, argument and value must be set. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + field: + description: |- + Field defines a field of the GraphQL query. + At least one of field, argument and value must be set. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + value: + description: |- + Value defines the value of an argument of the GraphQL query. + At least one of field, argument and value must be set. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + type: object + header: + description: |- + Header specifies to block requests containing a matching header. + Only one of parameter, header, path, pathSegment or json can be set. + properties: + name: + description: Name defines the name of a header. + properties: + matcher: + description: |- + NonInvertableCaseInsensitiveStringMatcher defines the way to match a string. + In comparison to a normal StringMatcher, a value is always matched ignoring the case and can't be inverted. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + value: + description: Value defines the value of a header. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + type: object + json: + description: |- + JSON specifies to block requests containing a matching JSON property in the body. + Only one of parameter, header, path, pathSegment or json can be set. + properties: + key: + description: Key defines the key of a JSON object. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + value: + description: Value defines the value of a JSON object. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + type: object + parameter: + description: |- + Parameter specifies to block requests containing a matching parameter. + Only one of parameter, header, path, pathSegment or json can be set. + properties: + name: + description: Name defines the name of a parameter. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + value: + description: Value defines the value of a parameter. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + type: object + path: + description: |- + Path specifies to block requests with a matching path. + Only one of parameter, header, path, pathSegment or json can be set. + properties: + matcher: + description: Matcher specifies which path to block. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + pathSegment: + description: |- + PathSegment specifies to block requests containing a matching path segment. + Only one of parameter, header, path, pathSegment or json can be set. + properties: + segments: + description: |- + Segments restricts which path segments are filtered by this rule. + If not specified, all segments of a path are filtered. + properties: + index: + description: Index restricts the rule to the path segment at this index (0-based). + minimum: 0 + type: integer + type: object + value: + description: Value specifies which path segment values to block. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + required: + - value + type: object + type: object + requestConditions: + description: RequestConditions defines additional request properties which must be matched in order for this rule to apply. + properties: + header: + description: Header defines the matching headers of a request. + properties: + name: + description: Name defines the name of a header. + properties: + matcher: + description: Matcher defines the way to match a string. In comparison to a normal StringMatcher, a value is always matched ignoring the case and can't be inverted. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + value: + description: Value defines the value of a header. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + type: object + invert: + default: false + description: Invert indicates whether the request condition should be inverted. + type: boolean + mediaType: + description: MediaType defines the matching media type from the content-type header of a request. + properties: + matcher: + description: |- + NonInvertableCaseInsensitiveStringMatcher defines the way to match a string. + In comparison to a normal StringMatcher, a value is always matched ignoring the case and can't be inverted. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + method: + description: Method defines the matching methods of a request. + items: + description: Method defines common HTTP methods. + enum: + - GET + - HEAD + - POST + - PUT + - PATCH + - DELETE + - CONNECT + - OPTIONS + - TRACE + type: string + type: array + path: + description: Path defines the matching path of a request. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + remoteIP: + description: RemoteIP defines the matching remote IPs of a request. + properties: + cidrRanges: + description: CIDRRanges defines the IPv4 or IPv6 CIDR ranges, e.g. ``196.148.3.128/26`` or ``2001:db8::/28``. + items: + description: CIDRRange defines an IPv4 or IPv6 CIDR range, e.g. “196.148.3.128/26“ or “2001:db8::/28“. + format: cidr + type: string + minItems: 1 + type: array + invert: + default: false + description: Invert indicates whether the match should be inverted. + type: boolean + required: + - cidrRanges + type: object + type: object + ruleKey: + description: RuleKey defines a technical key for the deny rule. Must be unique. + minLength: 1 + pattern: ^[A-Z][A-Z0-9_]*$ + type: string + threatHandlingMode: + default: Block + description: ThreatHandlingMode specifies how threats should be handled when a deny rule matches. + enum: + - Block + - LogOnly + type: string + required: + - blockData + - ruleKey + type: object + type: array + x-kubernetes-list-map-keys: + - ruleKey + x-kubernetes-list-type: map + type: object + type: object + type: object + type: object + served: true + storage: true + subresources: {} diff --git a/charts/airlock/microgateway/4.4.0/crds/envoyclusters.microgateway.airlock.com.yaml b/charts/airlock/microgateway/4.4.0/crds/envoyclusters.microgateway.airlock.com.yaml new file mode 100644 index 000000000..5d82a2547 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/crds/envoyclusters.microgateway.airlock.com.yaml @@ -0,0 +1,58 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + labels: + app.kubernetes.io/name: airlock-microgateway-operator + app.kubernetes.io/version: 4.4.0 + name: envoyclusters.microgateway.airlock.com +spec: + group: microgateway.airlock.com + names: + categories: + - airlock-microgateway + kind: EnvoyCluster + listKind: EnvoyClusterList + plural: envoyclusters + singular: envoycluster + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: EnvoyCluster is an additional Envoy Cluster resource which is added to those defined by the Airlock Microgateway. + 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: Specification of the desired additional Envoy cluster. + properties: + value: + description: Value defines the Envoy Cluster which is added to those configured by the Airlock Microgateway. + type: object + x-kubernetes-preserve-unknown-fields: true + type: object + type: object + served: true + storage: true + subresources: {} diff --git a/charts/airlock/microgateway/4.4.0/crds/envoyconfigurations.microgateway.airlock.com.yaml b/charts/airlock/microgateway/4.4.0/crds/envoyconfigurations.microgateway.airlock.com.yaml new file mode 100644 index 000000000..97c3a0384 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/crds/envoyconfigurations.microgateway.airlock.com.yaml @@ -0,0 +1,185 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + labels: + app.kubernetes.io/name: airlock-microgateway-operator + app.kubernetes.io/version: 4.4.0 + name: envoyconfigurations.microgateway.airlock.com +spec: + group: microgateway.airlock.com + names: + categories: + - airlock-microgateway + kind: EnvoyConfiguration + listKind: EnvoyConfigurationList + plural: envoyconfigurations + singular: envoyconfiguration + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + EnvoyConfiguration is the Schema for the envoyconfigurations API + {{% notice warning %}} EnvoyConfiguration resources may contain sensitive information and thus RBAC permissions should be granted with care. {{% /notice %}} + 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: EnvoyConfigurationSpec defines the desired state of EnvoyConfiguration + properties: + envoyResources: + properties: + clusters: + items: + type: object + x-kubernetes-preserve-unknown-fields: true + type: array + endpoints: + items: + type: object + x-kubernetes-preserve-unknown-fields: true + type: array + extensions: + items: + type: object + x-kubernetes-preserve-unknown-fields: true + type: array + listeners: + items: + type: object + x-kubernetes-preserve-unknown-fields: true + type: array + routes: + items: + type: object + x-kubernetes-preserve-unknown-fields: true + type: array + runtimes: + items: + type: object + x-kubernetes-preserve-unknown-fields: true + type: array + scopedRoutes: + items: + type: object + x-kubernetes-preserve-unknown-fields: true + type: array + secrets: + items: + type: object + x-kubernetes-preserve-unknown-fields: true + type: array + type: object + envoyResourcesRaw: + description: |- + EnvoyResourcesRaw defines the desired state for each resource type. The resources are stored as zstd compressed JSON bytes. + For debugging purposes, the resources can be inspected with the following command: `kubectl get envoyconfiguration -ojsonpath='{.spec.envoyResourcesRaw}' | base64 -d | zstd -d | jq` + format: byte + type: string + nodeID: + description: '**Deprecated:** This field is now ignored as NodeID is always derived from the resource name.' + type: string + type: object + status: + description: EnvoyConfigurationStatus defines the observed state of EnvoyConfiguration + properties: + conditions: + items: + properties: + lastTransitionTime: + description: 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 EnvoyConfiguration condition. + type: string + required: + - status + - type + type: object + type: array + status: + type: string + xds: + properties: + resourceTypes: + additionalProperties: + description: XdsResourceTypeSyncStatus defines the sync status of xDS for a specific resource type + properties: + errorMessage: + description: ErrorMessage defines an optional message why the currently served resources of this resource type are rejected by the client. + type: string + resources: + additionalProperties: + description: XdsResourceStatus defines the status of xDS for a specific resource + properties: + version: + description: Version defines the version which is currently served for this resource. + type: string + required: + - version + type: object + description: Resources defines the resources which are currently served for this resource type. + type: object + status: + description: Status defines the current sync status of this resource type. + type: string + version: + description: Version defines the version which is currently served for this resource type. + type: string + required: + - resources + - status + - version + type: object + description: ResourceTypes defines the sync statuses for each resource type. + type: object + version: + description: Version defines the version of the underlying xDS snapshot. + type: integer + required: + - version + type: object + required: + - status + - xds + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/charts/airlock/microgateway/4.4.0/crds/envoyhttpfilters.microgateway.airlock.com.yaml b/charts/airlock/microgateway/4.4.0/crds/envoyhttpfilters.microgateway.airlock.com.yaml new file mode 100644 index 000000000..7df49ba42 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/crds/envoyhttpfilters.microgateway.airlock.com.yaml @@ -0,0 +1,58 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + labels: + app.kubernetes.io/name: airlock-microgateway-operator + app.kubernetes.io/version: 4.4.0 + name: envoyhttpfilters.microgateway.airlock.com +spec: + group: microgateway.airlock.com + names: + categories: + - airlock-microgateway + kind: EnvoyHTTPFilter + listKind: EnvoyHTTPFilterList + plural: envoyhttpfilters + singular: envoyhttpfilter + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: EnvoyHTTPFilter is an additional Envoy HTTP Filter resource which is added to those defined by the Airlock Microgateway. + 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: Specification of the desired additional Envoy HTTP filter. + properties: + value: + description: Value defines the HTTP filter which is added to those configured by the Airlock Microgateway. + type: object + x-kubernetes-preserve-unknown-fields: true + type: object + type: object + served: true + storage: true + subresources: {} diff --git a/charts/airlock/microgateway/4.4.0/crds/graphqls.microgateway.airlock.com.yaml b/charts/airlock/microgateway/4.4.0/crds/graphqls.microgateway.airlock.com.yaml new file mode 100644 index 000000000..215cbb642 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/crds/graphqls.microgateway.airlock.com.yaml @@ -0,0 +1,88 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + labels: + app.kubernetes.io/name: airlock-microgateway-operator + app.kubernetes.io/version: 4.4.0 + name: graphqls.microgateway.airlock.com +spec: + group: microgateway.airlock.com + names: + categories: + - airlock-microgateway + kind: GraphQL + listKind: GraphQLList + plural: graphqls + singular: graphql + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: GraphQL contains the configuration for the GraphQL specification. + 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: Specification of the desired GraphQL specification. + properties: + settings: + description: Settings defines the settings to configure GraphQL. + properties: + allowIntrospection: + default: true + description: AllowIntrospection specifies if the introspection system is exposed. + type: boolean + allowMutations: + default: true + description: AllowMutations specifies if mutations are allowed. + type: boolean + schema: + description: Specifies the GraphQL schema. + properties: + source: + description: Source specifies the GraphQL schema to be enforced. + properties: + configMapRef: + description: ConfigMapRef references the configmap by its name containing the well-known key 'schema.graphql'. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + type: object + required: + - source + type: object + threatHandlingMode: + default: Block + description: ThreatHandlingMode specifies how threats should be handled. + enum: + - Block + - LogOnly + type: string + type: object + type: object + type: object + served: true + storage: true diff --git a/charts/airlock/microgateway/4.4.0/crds/headerrewrites.microgateway.airlock.com.yaml b/charts/airlock/microgateway/4.4.0/crds/headerrewrites.microgateway.airlock.com.yaml new file mode 100644 index 000000000..c7c4f7f63 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/crds/headerrewrites.microgateway.airlock.com.yaml @@ -0,0 +1,2083 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + labels: + app.kubernetes.io/name: airlock-microgateway-operator + app.kubernetes.io/version: 4.4.0 + name: headerrewrites.microgateway.airlock.com +spec: + group: microgateway.airlock.com + names: + categories: + - airlock-microgateway + kind: HeaderRewrites + listKind: HeaderRewritesList + plural: headerrewrites + singular: headerrewrites + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: HeaderRewrites is the Schema for the headerrewrites 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: Specification of the desired header rewriting behavior. + properties: + request: + description: Request defines manipulations on upstream request headers. + properties: + add: + description: Add defines which request headers will be added before forwarding to the upstream. + properties: + custom: + description: |- + Custom allows configuring additional upstream request headers. + Add selected headers. + items: + properties: + headers: + description: Headers to add. + items: + description: HeaderRewritesHeader specifies a header with a particular value + properties: + name: + description: Name defines the name of a header. + minLength: 1 + type: string + value: + description: Value defines the value of a header. + type: string + required: + - name + - value + type: object + minItems: 1 + type: array + mode: + default: AddIfAbsent + description: Mode defines the header addition strategy. + enum: + - AddIfAbsent + - OverwriteOrAdd + type: string + name: + description: Name describing the configured operation. + minLength: 1 + type: string + requestConditions: + description: RequestConditions defines additional request properties which must be matched in order for this operation to be applied. + properties: + header: + description: Header defines the matching headers of a request. + properties: + name: + description: Name defines the name of a header. + properties: + matcher: + description: Matcher defines the way to match a string. In comparison to a normal StringMatcher, a value is always matched ignoring the case and can't be inverted. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + value: + description: Value defines the value of a header. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + type: object + invert: + default: false + description: Invert indicates whether the request condition should be inverted. + type: boolean + mediaType: + description: MediaType defines the matching media type from the content-type header of a request. + properties: + matcher: + description: |- + NonInvertableCaseInsensitiveStringMatcher defines the way to match a string. + In comparison to a normal StringMatcher, a value is always matched ignoring the case and can't be inverted. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + method: + description: Method defines the matching methods of a request. + items: + description: Method defines common HTTP methods. + enum: + - GET + - HEAD + - POST + - PUT + - PATCH + - DELETE + - CONNECT + - OPTIONS + - TRACE + type: string + type: array + path: + description: Path defines the matching path of a request. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + remoteIP: + description: RemoteIP defines the matching remote IPs of a request. + properties: + cidrRanges: + description: CIDRRanges defines the IPv4 or IPv6 CIDR ranges, e.g. ``196.148.3.128/26`` or ``2001:db8::/28``. + items: + description: CIDRRange defines an IPv4 or IPv6 CIDR range, e.g. “196.148.3.128/26“ or “2001:db8::/28“. + format: cidr + type: string + minItems: 1 + type: array + invert: + default: false + description: Invert indicates whether the match should be inverted. + type: boolean + required: + - cidrRanges + type: object + type: object + required: + - headers + - name + type: object + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + allow: + description: |- + Allow defines which request headers will be forwarded to the upstream. + This can either be allHeaders or matchingHeaders. + Default: matchingHeaders: {...} + properties: + allHeaders: + description: AllHeaders specifies that all request headers should be forwarded. + type: object + matchingHeaders: + description: MatchingHeaders specifies which request headers should be forwarded. + properties: + builtIn: + description: BuiltIn allows configuring a set of predefined upstream request headers. + properties: + standardHeaders: + default: true + description: StandardHeaders defines whether the request headers which are forwarded to the upstream will be restricted to a set of common request headers. + type: boolean + tracingHeaders: + default: false + description: TracingHeaders defines whether to allow common tracing headers to be forwarded to the upstream. + type: boolean + type: object + custom: + description: Custom allows configuring additional upstream request headers. + items: + properties: + headers: + description: Headers to allow. + items: + description: |- + HeaderMatcher defines a matcher for an HTTP header. + At least one of name and value must be set. + properties: + name: + description: Name defines the name of a header. + properties: + matcher: + description: Matcher defines the way to match a string. In comparison to a normal StringMatcher, a value is always matched ignoring the case and can't be inverted. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + value: + description: Value defines the value of a header. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + type: object + minItems: 1 + type: array + name: + description: Name describing the configured operation. Must be unique. + minLength: 1 + type: string + requestConditions: + description: RequestConditions defines additional request properties which must be matched in order for this operation to be applied. + properties: + header: + description: Header defines the matching headers of a request. + properties: + name: + description: Name defines the name of a header. + properties: + matcher: + description: Matcher defines the way to match a string. In comparison to a normal StringMatcher, a value is always matched ignoring the case and can't be inverted. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + value: + description: Value defines the value of a header. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + type: object + invert: + default: false + description: Invert indicates whether the request condition should be inverted. + type: boolean + mediaType: + description: MediaType defines the matching media type from the content-type header of a request. + properties: + matcher: + description: |- + NonInvertableCaseInsensitiveStringMatcher defines the way to match a string. + In comparison to a normal StringMatcher, a value is always matched ignoring the case and can't be inverted. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + method: + description: Method defines the matching methods of a request. + items: + description: Method defines common HTTP methods. + enum: + - GET + - HEAD + - POST + - PUT + - PATCH + - DELETE + - CONNECT + - OPTIONS + - TRACE + type: string + type: array + path: + description: Path defines the matching path of a request. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + remoteIP: + description: RemoteIP defines the matching remote IPs of a request. + properties: + cidrRanges: + description: CIDRRanges defines the IPv4 or IPv6 CIDR ranges, e.g. ``196.148.3.128/26`` or ``2001:db8::/28``. + items: + description: CIDRRange defines an IPv4 or IPv6 CIDR range, e.g. “196.148.3.128/26“ or “2001:db8::/28“. + format: cidr + type: string + minItems: 1 + type: array + invert: + default: false + description: Invert indicates whether the match should be inverted. + type: boolean + required: + - cidrRanges + type: object + type: object + required: + - headers + - name + type: object + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: object + remove: + description: Remove defines which request headers will be removed before forwarding to the upstream. + properties: + builtIn: + description: BuiltIn allows configuring a set of predefined upstream request headers. + properties: + alternativeForwardedHeaders: + default: true + description: |- + AlternativeForwardedHeaders removes downstream request headers which could potentially + be abused to alter the upstream's view of the remote connection. + type: boolean + type: object + custom: + description: Custom allows configuring additional upstream request headers. + items: + properties: + headers: + description: Headers to remove. + items: + description: |- + HeaderMatcher defines a matcher for an HTTP header. + At least one of name and value must be set. + properties: + name: + description: Name defines the name of a header. + properties: + matcher: + description: Matcher defines the way to match a string. In comparison to a normal StringMatcher, a value is always matched ignoring the case and can't be inverted. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + value: + description: Value defines the value of a header. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + type: object + minItems: 1 + type: array + name: + description: Name describing the configured operation. Must be unique. + minLength: 1 + type: string + requestConditions: + description: RequestConditions defines additional request properties which must be matched in order for this operation to be applied. + properties: + header: + description: Header defines the matching headers of a request. + properties: + name: + description: Name defines the name of a header. + properties: + matcher: + description: Matcher defines the way to match a string. In comparison to a normal StringMatcher, a value is always matched ignoring the case and can't be inverted. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + value: + description: Value defines the value of a header. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + type: object + invert: + default: false + description: Invert indicates whether the request condition should be inverted. + type: boolean + mediaType: + description: MediaType defines the matching media type from the content-type header of a request. + properties: + matcher: + description: |- + NonInvertableCaseInsensitiveStringMatcher defines the way to match a string. + In comparison to a normal StringMatcher, a value is always matched ignoring the case and can't be inverted. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + method: + description: Method defines the matching methods of a request. + items: + description: Method defines common HTTP methods. + enum: + - GET + - HEAD + - POST + - PUT + - PATCH + - DELETE + - CONNECT + - OPTIONS + - TRACE + type: string + type: array + path: + description: Path defines the matching path of a request. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + remoteIP: + description: RemoteIP defines the matching remote IPs of a request. + properties: + cidrRanges: + description: CIDRRanges defines the IPv4 or IPv6 CIDR ranges, e.g. ``196.148.3.128/26`` or ``2001:db8::/28``. + items: + description: CIDRRange defines an IPv4 or IPv6 CIDR range, e.g. “196.148.3.128/26“ or “2001:db8::/28“. + format: cidr + type: string + minItems: 1 + type: array + invert: + default: false + description: Invert indicates whether the match should be inverted. + type: boolean + required: + - cidrRanges + type: object + type: object + required: + - headers + - name + type: object + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: object + response: + description: Response defines manipulations on upstream response headers. + properties: + add: + description: Add defines which response headers will be added before forwarding to the downstream. + properties: + builtIn: + description: BuiltIn allows configuring a set of predefined upstream response headers. + properties: + csp: + default: true + description: |- + CSP sets a content security policy which allows only same-origin requests except for images + if the 'Content-Security-Policy' header is not set by the upstream. + type: boolean + featurePolicy: + default: false + description: |- + FeaturePolicy sets a feature policy which prevents cross-origin use of several browser features + if the 'Feature-Policy' header is not set by the upstream. + **Deprecated:** Use permissionsPolicy instead. + type: boolean + hsts: + default: true + description: HSTS enforces the use of HTTPS if the 'Strict-Transport-Security' header is not already set by the upstream. + type: boolean + hstsPreload: + default: false + description: HSTSPreload enforces the use of HTTPS including for subdomains and enables HSTS preload. + type: boolean + permissionsPolicy: + default: true + description: |- + PermissionsPolicy sets a permissions policy which prevents cross-origin use of several browser features + if the 'Permissions-Policy' header is not set by the upstream. + type: boolean + referrerPolicy: + default: true + description: |- + ReferrerPolicy ensures that no 'Referer' header is sent for cross-origin requests + if the 'Referrer-Policy' header is not set by the upstream. + type: boolean + xContentTypeOptions: + default: true + description: XContentTypeOptions sets 'X-Content-Type-Options' to 'nosniff' if it is not set by the upstream. + type: boolean + xFrameOptions: + default: true + description: XFrameOptions sets 'X-Frame-Options' to SAMEORIGIN if it is not set by the upstream. + type: boolean + type: object + custom: + description: Custom allows configuring additional upstream response headers. + items: + properties: + headers: + description: Headers to add. + items: + description: HeaderRewritesHeader specifies a header with a particular value + properties: + name: + description: Name defines the name of a header. + minLength: 1 + type: string + value: + description: Value defines the value of a header. + type: string + required: + - name + - value + type: object + minItems: 1 + type: array + mode: + default: AddIfAbsent + description: Mode defines the header addition strategy. + enum: + - AddIfAbsent + - OverwriteOrAdd + type: string + name: + description: Name describing the configured operation. + minLength: 1 + type: string + requestConditions: + description: RequestConditions defines additional request properties which must be matched in order for this operation to be applied. + properties: + header: + description: Header defines the matching headers of a request. + properties: + name: + description: Name defines the name of a header. + properties: + matcher: + description: Matcher defines the way to match a string. In comparison to a normal StringMatcher, a value is always matched ignoring the case and can't be inverted. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + value: + description: Value defines the value of a header. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + type: object + invert: + default: false + description: Invert indicates whether the request condition should be inverted. + type: boolean + mediaType: + description: MediaType defines the matching media type from the content-type header of a request. + properties: + matcher: + description: |- + NonInvertableCaseInsensitiveStringMatcher defines the way to match a string. + In comparison to a normal StringMatcher, a value is always matched ignoring the case and can't be inverted. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + method: + description: Method defines the matching methods of a request. + items: + description: Method defines common HTTP methods. + enum: + - GET + - HEAD + - POST + - PUT + - PATCH + - DELETE + - CONNECT + - OPTIONS + - TRACE + type: string + type: array + path: + description: Path defines the matching path of a request. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + remoteIP: + description: RemoteIP defines the matching remote IPs of a request. + properties: + cidrRanges: + description: CIDRRanges defines the IPv4 or IPv6 CIDR ranges, e.g. ``196.148.3.128/26`` or ``2001:db8::/28``. + items: + description: CIDRRange defines an IPv4 or IPv6 CIDR range, e.g. “196.148.3.128/26“ or “2001:db8::/28“. + format: cidr + type: string + minItems: 1 + type: array + invert: + default: false + description: Invert indicates whether the match should be inverted. + type: boolean + required: + - cidrRanges + type: object + type: object + required: + - headers + - name + type: object + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + allow: + description: |- + Allow defines which response headers will be forwarded to the downstream. + This can either be allHeaders or matchingHeaders. + Default: allHeaders: {} + properties: + allHeaders: + description: AllHeaders specifies that all response headers should be forwarded. + type: object + matchingHeaders: + description: MatchingHeaders specifies which response headers should be forwarded. + properties: + builtIn: + description: BuiltIn allows configuring a set of predefined upstream response header. + properties: + standardHeaders: + default: false + description: StandardHeaders defines whether the response headers which are forwarded to the downstream will be restricted to a set of common response headers. + type: boolean + type: object + custom: + description: Custom allows configuring additional upstream response headers. + items: + properties: + headers: + description: Headers to allow. + items: + description: |- + HeaderMatcher defines a matcher for an HTTP header. + At least one of name and value must be set. + properties: + name: + description: Name defines the name of a header. + properties: + matcher: + description: Matcher defines the way to match a string. In comparison to a normal StringMatcher, a value is always matched ignoring the case and can't be inverted. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + value: + description: Value defines the value of a header. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + type: object + minItems: 1 + type: array + name: + description: Name describing the configured operation. Must be unique. + minLength: 1 + type: string + requestConditions: + description: RequestConditions defines additional request properties which must be matched in order for this operation to be applied. + properties: + header: + description: Header defines the matching headers of a request. + properties: + name: + description: Name defines the name of a header. + properties: + matcher: + description: Matcher defines the way to match a string. In comparison to a normal StringMatcher, a value is always matched ignoring the case and can't be inverted. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + value: + description: Value defines the value of a header. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + type: object + invert: + default: false + description: Invert indicates whether the request condition should be inverted. + type: boolean + mediaType: + description: MediaType defines the matching media type from the content-type header of a request. + properties: + matcher: + description: |- + NonInvertableCaseInsensitiveStringMatcher defines the way to match a string. + In comparison to a normal StringMatcher, a value is always matched ignoring the case and can't be inverted. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + method: + description: Method defines the matching methods of a request. + items: + description: Method defines common HTTP methods. + enum: + - GET + - HEAD + - POST + - PUT + - PATCH + - DELETE + - CONNECT + - OPTIONS + - TRACE + type: string + type: array + path: + description: Path defines the matching path of a request. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + remoteIP: + description: RemoteIP defines the matching remote IPs of a request. + properties: + cidrRanges: + description: CIDRRanges defines the IPv4 or IPv6 CIDR ranges, e.g. ``196.148.3.128/26`` or ``2001:db8::/28``. + items: + description: CIDRRange defines an IPv4 or IPv6 CIDR range, e.g. “196.148.3.128/26“ or “2001:db8::/28“. + format: cidr + type: string + minItems: 1 + type: array + invert: + default: false + description: Invert indicates whether the match should be inverted. + type: boolean + required: + - cidrRanges + type: object + type: object + required: + - headers + - name + type: object + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: object + remove: + description: Remove defines which response headers will be removed before forwarding to the downstream. + properties: + builtIn: + description: BuiltIn allows configuring a set of predefined upstream response headers. + properties: + auth: + description: Auth defines the categories of headers concerning authentication. + properties: + basic: + default: false + description: Basic removes upstream response headers that advise clients to authenticate with Basic Authentication. + type: boolean + negotiate: + default: true + description: Negotiate removes upstream response headers that advise clients to authenticate with Negotiate. + type: boolean + ntlm: + default: true + description: |- + NTLM removes upstream response headers that advise clients to authenticate with NTLM. + By default, these headers are removed, because NTLM pass-through is not supported. + type: boolean + type: object + informationLeakage: + description: InformationLeakage defines the categories of headers concerning information leakage. + properties: + application: + default: true + description: Application removes upstream response headers that leak information about the deployed software. + type: boolean + server: + default: true + description: Server removes upstream response headers that leak information about the server. + type: boolean + type: object + permissiveCors: + default: true + description: PermissiveCORS removes upstream response headers for CORS (Cross-Origin Resource Sharing) which have no restrictions and therefore reduce client-side security. + type: boolean + type: object + custom: + description: Custom allows configuring additional upstream response headers. + items: + properties: + headers: + description: Headers to remove. + items: + description: |- + HeaderMatcher defines a matcher for an HTTP header. + At least one of name and value must be set. + properties: + name: + description: Name defines the name of a header. + properties: + matcher: + description: Matcher defines the way to match a string. In comparison to a normal StringMatcher, a value is always matched ignoring the case and can't be inverted. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + value: + description: Value defines the value of a header. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + type: object + minItems: 1 + type: array + name: + description: Name describing the configured remove operation. Must be unique. + minLength: 1 + type: string + requestConditions: + description: RequestConditions defines additional request properties which must be matched in order for this operation to be applied. + properties: + header: + description: Header defines the matching headers of a request. + properties: + name: + description: Name defines the name of a header. + properties: + matcher: + description: Matcher defines the way to match a string. In comparison to a normal StringMatcher, a value is always matched ignoring the case and can't be inverted. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + value: + description: Value defines the value of a header. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + type: object + invert: + default: false + description: Invert indicates whether the request condition should be inverted. + type: boolean + mediaType: + description: MediaType defines the matching media type from the content-type header of a request. + properties: + matcher: + description: |- + NonInvertableCaseInsensitiveStringMatcher defines the way to match a string. + In comparison to a normal StringMatcher, a value is always matched ignoring the case and can't be inverted. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + method: + description: Method defines the matching methods of a request. + items: + description: Method defines common HTTP methods. + enum: + - GET + - HEAD + - POST + - PUT + - PATCH + - DELETE + - CONNECT + - OPTIONS + - TRACE + type: string + type: array + path: + description: Path defines the matching path of a request. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + remoteIP: + description: RemoteIP defines the matching remote IPs of a request. + properties: + cidrRanges: + description: CIDRRanges defines the IPv4 or IPv6 CIDR ranges, e.g. ``196.148.3.128/26`` or ``2001:db8::/28``. + items: + description: CIDRRange defines an IPv4 or IPv6 CIDR range, e.g. “196.148.3.128/26“ or “2001:db8::/28“. + format: cidr + type: string + minItems: 1 + type: array + invert: + default: false + description: Invert indicates whether the match should be inverted. + type: boolean + required: + - cidrRanges + type: object + type: object + required: + - headers + - name + type: object + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: object + settings: + description: Settings configures the HeaderRewrites filter. + properties: + operationalMode: + default: Production + description: OperationalMode defines the behavior of the filter. In integration mode more information is logged about the requests and responses. + enum: + - Production + - Integration + type: string + type: object + type: object + type: object + served: true + storage: true diff --git a/charts/airlock/microgateway/4.4.0/crds/identitypropagations.microgateway.airlock.com.yaml b/charts/airlock/microgateway/4.4.0/crds/identitypropagations.microgateway.airlock.com.yaml new file mode 100644 index 000000000..4a5df8ed0 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/crds/identitypropagations.microgateway.airlock.com.yaml @@ -0,0 +1,151 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + labels: + app.kubernetes.io/name: airlock-microgateway-operator + app.kubernetes.io/version: 4.4.0 + name: identitypropagations.microgateway.airlock.com +spec: + group: microgateway.airlock.com + names: + categories: + - airlock-microgateway + kind: IdentityPropagation + listKind: IdentityPropagationList + plural: identitypropagations + singular: identitypropagation + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: IdentityPropagation specifies the desired identity propagation. + 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: Specification of the desired identity propagation. + properties: + bearerToken: + description: BearerToken configures identity propagation via an authorization header containing a bearer token. + properties: + source: + description: Source from which to extract the token. + properties: + metadata: + description: Metadata specifies to extract a value from an Envoy dynamic filter metadata key. + properties: + key: + description: Key specifies the metadata key from which to load the value, e.g. `some_payload.aud`. + minLength: 1 + type: string + namespace: + description: Namespace specifies the metadata namespace within which the lookup should be performed, e.g. `envoy.filters.http.jwt_authn`. + minLength: 1 + type: string + required: + - key + - namespace + type: object + oidc: + description: OIDC specifies to extract a value from the result of an OpenID Connect flow. + properties: + accessToken: + description: AccessToken specifies to extract the value from the OpenID Connect Access Token. + type: object + idToken: + description: IDToken specifies to extract the value from the OpenID Connect ID Token. + properties: + claim: + description: Claim selects the JWT claim from which to extract the value. + minLength: 1 + type: string + required: + - claim + type: object + type: object + type: object + required: + - source + type: object + header: + description: Header configures identity propagation via a request header. + properties: + name: + description: Name of the header to set. + minLength: 1 + type: string + value: + description: Value to propagate to the application. + properties: + source: + description: Source from which to extract the value. + properties: + metadata: + description: Metadata specifies to extract a value from an Envoy dynamic filter metadata key. + properties: + key: + description: Key specifies the metadata key from which to load the value, e.g. `some_payload.aud`. + minLength: 1 + type: string + namespace: + description: Namespace specifies the metadata namespace within which the lookup should be performed, e.g. `envoy.filters.http.jwt_authn`. + minLength: 1 + type: string + required: + - key + - namespace + type: object + oidc: + description: OIDC specifies to extract a value from the result of an OpenID Connect flow. + properties: + accessToken: + description: AccessToken specifies to extract the value from the OpenID Connect Access Token. + type: object + idToken: + description: IDToken specifies to extract the value from the OpenID Connect ID Token. + properties: + claim: + description: Claim selects the JWT claim from which to extract the value. + minLength: 1 + type: string + required: + - claim + type: object + type: object + type: object + required: + - source + type: object + required: + - name + - value + type: object + type: object + required: + - spec + type: object + served: true + storage: true + subresources: {} diff --git a/charts/airlock/microgateway/4.4.0/crds/jwks.microgateway.airlock.com.yaml b/charts/airlock/microgateway/4.4.0/crds/jwks.microgateway.airlock.com.yaml new file mode 100644 index 000000000..4ea621381 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/crds/jwks.microgateway.airlock.com.yaml @@ -0,0 +1,294 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + labels: + app.kubernetes.io/name: airlock-microgateway-operator + app.kubernetes.io/version: 4.4.0 + name: jwks.microgateway.airlock.com +spec: + group: microgateway.airlock.com + names: + categories: + - airlock-microgateway + kind: JWKS + listKind: JWKSList + plural: jwks + singular: jwks + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: JWKS provides a JSON Web Key Set. + 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: Specification of the JWKS. + properties: + provider: + description: Provider configures the source from which to retrieve the JWKS. + properties: + local: + description: Local specifies to retrieve the JWKS from a local secret. + properties: + secretRef: + description: SecretRef selects the secret containing the JWKS under the key 'jwks.json'. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + required: + - secretRef + type: object + remote: + description: Remote specifies to retrieve the JWKS from a remote endpoint. + properties: + timeouts: + description: Timeouts specifies the timeouts when interacting with the Token endpoint. + properties: + connect: + default: 5s + description: Connect specifies the timeout for establishing a connection. + type: string + maxDuration: + default: 15s + description: MaxDuration specifies the response timeout. + type: string + type: object + tls: + description: TLS defines TLS settings. + properties: + certificateVerification: + description: CertificateVerification specifies how the certificate presented by the server is verified. + properties: + custom: + description: |- + Custom explicitly specifies how the server certificate should be verified. + Typical use cases include specifying a custom CA and SAN match when working with self-signed certificates or pinning a specific public key. + properties: + allowedSANs: + description: |- + AllowedSANs is a list of matchers to verify the Subject Alternative name. If specified, it will verify that the + Subject Alternative Name of the presented certificate matches one of the specified matchers. The matching uses “any” semantics, + that is to say, the SAN is verified if at least one matcher is matched. + AllowedSANs requires trustedCA to be set. + items: + description: |- + TLSValidationContextSANMatcher is a list of matchers to verify the Subject Alternative name. If specified, it will verify that the + Subject Alternative Name of the presented certificate matches one of the specified matchers. + properties: + matcher: + description: Matcher defines the string matcher for the SAN value. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + sanType: + description: SanType defines the type of SAN matcher. + enum: + - DNS + - Email + - URI + - IPAddress + type: string + required: + - matcher + - sanType + type: object + minItems: 1 + type: array + certificatePinning: + description: |- + CertificatePinning defines constraints the presented certificate must fulfill. + If more than one constraint is configured only one must be satisfied. + At least one of allowedSPKIs and allowedHashes must be set. + properties: + allowedHashes: + description: |- + AllowedHashes is a list of hex-encoded SHA-256 hashes. + If specified, it will verify that the SHA-256 of the DER-encoded presented certificate matches one of the specified values. + items: + type: string + minItems: 1 + type: array + allowedSPKIs: + description: |- + AllowedSPKIs is a list of base64-encoded SHA-256 hashes. + If specified, it will verify that the SHA-256 of the DER-encoded Subject Public Key Information (SPKI) of the presented certificate matches one of the specified values. + items: + type: string + minItems: 1 + type: array + type: object + crl: + description: CRL defines the Certificate Revocation List (CRL) settings. + properties: + lists: + description: Lists defines the list of secretRefs containing Certificate Revocation Lists. + items: + properties: + secretRef: + description: SecretRef defines the reference to a secret containing one or more CRL's (in PEM format) under the key 'ca.crl'. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + required: + - secretRef + type: object + minItems: 1 + type: array + validationMode: + default: VerifyChain + description: ValidationMode defines whether only the leaf certificate or also the CA certs should be checked. + enum: + - VerifyLeafCertOnly + - VerifyChain + type: string + type: object + trustedCA: + description: TrustedCA defines which CA certificates are trusted. + properties: + certificates: + description: Certificates defines the list of secretRefs containing trusted CA certificates. + items: + properties: + secretRef: + description: SecretRef defines the reference to a secret containing one or more CA certificates under the key 'ca.crt'. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + required: + - secretRef + type: object + minItems: 1 + type: array + verificationDepth: + default: 1 + description: |- + VerificationDepth specifies the hops in the certificate chain at which validation is performed. + 1 means that either the leaf or the signing CA must be in the set of trusted certificates. + format: int32 + type: integer + required: + - certificates + type: object + type: object + disabled: + description: |- + Disabled specifies to trust any certificate without verification. + THIS IS INSECURE AND SHOULD ONLY BE USED FOR TESTING. + type: object + publicCAs: + description: PublicCAs specifies to only accept certificates with a SAN matching "uri" and which are signed by a CA which is either directly or indirectly trusted by any of the root CA certificates shipped with the Airlock Microgateway Engine's base image. + type: object + type: object + ciphers: + description: Ciphers defines a list of the supported TLS cipher suites. For details on cipher list refer to the envoy documentation on cipher_suites in common tls configuration. + items: + type: string + minItems: 1 + type: array + protocol: + description: Protocol defines the supported TLS protocol versions. + properties: + maximum: + description: Maximum supported TLS version. + enum: + - TLSv1_0 + - TLSv1_1 + - TLSv1_2 + - TLSv1_3 + type: string + minimum: + description: Minimum supported TLS version. + enum: + - TLSv1_0 + - TLSv1_1 + - TLSv1_2 + - TLSv1_3 + type: string + type: object + type: object + uri: + description: URI specifies the endpoint address. + format: uri + minLength: 1 + pattern: ^(http|https)://.*$ + type: string + required: + - uri + type: object + type: object + required: + - provider + type: object + required: + - spec + type: object + served: true + storage: true diff --git a/charts/airlock/microgateway/4.4.0/crds/limits.microgateway.airlock.com.yaml b/charts/airlock/microgateway/4.4.0/crds/limits.microgateway.airlock.com.yaml new file mode 100644 index 000000000..3f3cae2ee --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/crds/limits.microgateway.airlock.com.yaml @@ -0,0 +1,651 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + labels: + app.kubernetes.io/name: airlock-microgateway-operator + app.kubernetes.io/version: 4.4.0 + name: limits.microgateway.airlock.com +spec: + group: microgateway.airlock.com + names: + categories: + - airlock-microgateway + kind: Limits + listKind: LimitsList + plural: limits + singular: limits + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Limits contains the configuration for limits. + 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: Specification of the desired limits behavior. + properties: + request: + description: Request defines the limits for requests. + properties: + limited: + description: Limited enables limits on request scope. + properties: + exceptions: + description: Exceptions defines limit exceptions. + items: + description: LimitsException defines an exception for limits. + properties: + length: + description: Length defines an exception for length limits based on the data element exceeding the limit. + properties: + graphQL: + description: GraphQL defines a field, argument or value length limit exception for a GraphQL query. + properties: + argument: + description: |- + Argument restricts the exception to GraphQL queries with a matching argument of a field. + At least one of field, argument and value must be set. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + field: + description: |- + Field restricts the exception to GraphQL queries with a matching field. + At least one of field, argument and value must be set. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + value: + description: |- + Value restricts the exception to GraphQL queries with a matching argument value. + At least one of field, argument and value must be set. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + type: object + json: + description: JSON defines a key and value length limit exception for a JSON property. + properties: + jsonPath: + description: |- + JSONPath restricts the exception to JSON properties with a matching JSONPath. + Expressions in JSONPath i.e. `?(expr)` are not supported. + minLength: 1 + type: string + required: + - jsonPath + type: object + parameter: + description: Parameter defines a name and value length limit exception for a parameter. + properties: + name: + description: Name restricts the exception to parameters with a matching name. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + source: + default: Any + description: Source restricts the exception to parameters of this kind. + enum: + - Query + - Post + - Any + type: string + required: + - name + type: object + type: object + requestConditions: + description: RequestConditions defines additional request properties which must be matched in order for this exception to apply. + properties: + header: + description: Header defines the matching headers of a request. + properties: + name: + description: Name defines the name of a header. + properties: + matcher: + description: Matcher defines the way to match a string. In comparison to a normal StringMatcher, a value is always matched ignoring the case and can't be inverted. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + value: + description: Value defines the value of a header. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + type: object + invert: + default: false + description: Invert indicates whether the request condition should be inverted. + type: boolean + mediaType: + description: MediaType defines the matching media type from the content-type header of a request. + properties: + matcher: + description: |- + NonInvertableCaseInsensitiveStringMatcher defines the way to match a string. + In comparison to a normal StringMatcher, a value is always matched ignoring the case and can't be inverted. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + method: + description: Method defines the matching methods of a request. + items: + description: Method defines common HTTP methods. + enum: + - GET + - HEAD + - POST + - PUT + - PATCH + - DELETE + - CONNECT + - OPTIONS + - TRACE + type: string + type: array + path: + description: Path defines the matching path of a request. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + remoteIP: + description: RemoteIP defines the matching remote IPs of a request. + properties: + cidrRanges: + description: CIDRRanges defines the IPv4 or IPv6 CIDR ranges, e.g. ``196.148.3.128/26`` or ``2001:db8::/28``. + items: + description: CIDRRange defines an IPv4 or IPv6 CIDR range, e.g. “196.148.3.128/26“ or “2001:db8::/28“. + format: cidr + type: string + minItems: 1 + type: array + invert: + default: false + description: Invert indicates whether the match should be inverted. + type: boolean + required: + - cidrRanges + type: object + type: object + type: object + type: array + general: + description: General defines general request limits. + properties: + bodySize: + anyOf: + - type: integer + - type: string + default: 100Mi + description: BodySize limits the total size of the request body. It specifies the number of bytes (0 = unlimited). This limit is effective for any request not processed by one of the content parsers (e.g. json) as configured in the Parser CRD. **Note** This limit does not apply to WebSocket or gRPC traffic. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + pathLength: + anyOf: + - type: integer + - type: string + default: 1Ki + description: PathLength defines the maximum path length for all requests (parsed and unparsed). + 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 + graphQL: + description: GraphQL defines the limits for GraphQL requests. + properties: + nestingDepth: + default: 10 + description: NestingDepth defines the maximum depth of nesting for GraphQL objects. + format: int64 + type: integer + querySize: + anyOf: + - type: integer + - type: string + default: 1Ki + description: QuerySize defines the maximum size for GraphQL queries. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + valueLength: + anyOf: + - type: integer + - type: string + default: "256" + description: ValueLength defines the maximum length for GraphQL values. + 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 + json: + description: JSON defines the limits for JSON requests. + properties: + bodySize: + anyOf: + - type: integer + - type: string + default: 100Ki + description: BodySize limits the total size of the JSON request body. It specifies the number of bytes (0 = unlimited). + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + elementCount: + default: 10000 + description: ElementCount defines the maximum number of keys and array items in the whole JSON document (recursive). + format: int64 + type: integer + keyCount: + default: 250 + description: KeyCount defines the maximum number of keys of a single JSON object (non-recursive). + format: int64 + type: integer + keyLength: + anyOf: + - type: integer + - type: string + default: "128" + description: KeyLength defines the maximum length for JSON keys. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + nestingDepth: + default: 100 + description: NestingDepth defines the maximum depth of nesting for JSON objects and JSON arrays. + format: int64 + type: integer + valueLength: + anyOf: + - type: integer + - type: string + default: 8Ki + description: ValueLength defines the maximum length for JSON values. + 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 + multipart: + description: Multipart defines the limits for Multipart requests. + properties: + bodySize: + anyOf: + - type: integer + - type: string + default: 100Mi + description: BodySize limits the total size of the Multipart request body. It specifies the number of bytes (0 = unlimited). + 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 + parameter: + description: Parameter defines the limits for request parameters. + properties: + bodySize: + anyOf: + - type: integer + - type: string + default: 100Ki + description: BodySize limits the total size of the form data body. It specifies the number of bytes (0 = unlimited). + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + count: + default: 128 + description: Count defines the maximum number of request parameters. + format: int64 + type: integer + nameLength: + anyOf: + - type: integer + - type: string + default: "128" + description: NameLength defines the maximum length for parameter names. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + valueLength: + anyOf: + - type: integer + - type: string + default: 8Ki + description: ValueLength defines the maximum length for parameter values. + 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 + type: object + unlimited: + description: Unlimited disables all limits on request scope. + type: object + type: object + settings: + description: Settings configures the limits filter. + properties: + threatHandlingMode: + default: Block + description: ThreatHandlingMode specifies how threats should be handled when a limit hits. + enum: + - Block + - LogOnly + type: string + type: object + type: object + type: object + served: true + storage: true diff --git a/charts/airlock/microgateway/4.4.0/crds/oidcproviders.microgateway.airlock.com.yaml b/charts/airlock/microgateway/4.4.0/crds/oidcproviders.microgateway.airlock.com.yaml new file mode 100644 index 000000000..a4b737db8 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/crds/oidcproviders.microgateway.airlock.com.yaml @@ -0,0 +1,342 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + labels: + app.kubernetes.io/name: airlock-microgateway-operator + app.kubernetes.io/version: 4.4.0 + name: oidcproviders.microgateway.airlock.com +spec: + group: microgateway.airlock.com + names: + categories: + - airlock-microgateway + kind: OIDCProvider + listKind: OIDCProviderList + plural: oidcproviders + singular: oidcprovider + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + OIDCProvider specifies an OpenID Provider (OP). + + {{% notice info %}} The OIDC feature requires SessionHandling to be configured in the SidecarGateway. {{% /notice %}} + 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: Specification of an OpenID Provider. + properties: + static: + description: Static configures an OpenID Provider by explicitly specifying all endpoints. + properties: + endpoints: + description: Endpoints specifies the OpenID Provider endpoints. + properties: + authorization: + description: Authorization specifies the endpoint to which the authorization request is sent. + properties: + uri: + description: URI specifies the endpoint address. + format: uri + minLength: 1 + pattern: ^(http|https)://.*$ + type: string + required: + - uri + type: object + token: + description: Token configures the endpoint from which the access, ID and refresh tokens are obtained. + properties: + timeouts: + description: Timeouts specifies the timeouts when interacting with the Token endpoint. + properties: + connect: + default: 5s + description: Connect specifies the timeout for establishing a connection. + type: string + maxDuration: + default: 15s + description: MaxDuration specifies the response timeout. + type: string + type: object + tls: + description: TLS defines TLS settings. + properties: + certificateVerification: + description: CertificateVerification specifies how the certificate presented by the server is verified. + properties: + custom: + description: |- + Custom explicitly specifies how the server certificate should be verified. + Typical use cases include specifying a custom CA and SAN match when working with self-signed certificates or pinning a specific public key. + properties: + allowedSANs: + description: |- + AllowedSANs is a list of matchers to verify the Subject Alternative name. If specified, it will verify that the + Subject Alternative Name of the presented certificate matches one of the specified matchers. The matching uses “any” semantics, + that is to say, the SAN is verified if at least one matcher is matched. + AllowedSANs requires trustedCA to be set. + items: + description: |- + TLSValidationContextSANMatcher is a list of matchers to verify the Subject Alternative name. If specified, it will verify that the + Subject Alternative Name of the presented certificate matches one of the specified matchers. + properties: + matcher: + description: Matcher defines the string matcher for the SAN value. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + sanType: + description: SanType defines the type of SAN matcher. + enum: + - DNS + - Email + - URI + - IPAddress + type: string + required: + - matcher + - sanType + type: object + minItems: 1 + type: array + certificatePinning: + description: |- + CertificatePinning defines constraints the presented certificate must fulfill. + If more than one constraint is configured only one must be satisfied. + At least one of allowedSPKIs and allowedHashes must be set. + properties: + allowedHashes: + description: |- + AllowedHashes is a list of hex-encoded SHA-256 hashes. + If specified, it will verify that the SHA-256 of the DER-encoded presented certificate matches one of the specified values. + items: + type: string + minItems: 1 + type: array + allowedSPKIs: + description: |- + AllowedSPKIs is a list of base64-encoded SHA-256 hashes. + If specified, it will verify that the SHA-256 of the DER-encoded Subject Public Key Information (SPKI) of the presented certificate matches one of the specified values. + items: + type: string + minItems: 1 + type: array + type: object + crl: + description: CRL defines the Certificate Revocation List (CRL) settings. + properties: + lists: + description: Lists defines the list of secretRefs containing Certificate Revocation Lists. + items: + properties: + secretRef: + description: SecretRef defines the reference to a secret containing one or more CRL's (in PEM format) under the key 'ca.crl'. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + required: + - secretRef + type: object + minItems: 1 + type: array + validationMode: + default: VerifyChain + description: ValidationMode defines whether only the leaf certificate or also the CA certs should be checked. + enum: + - VerifyLeafCertOnly + - VerifyChain + type: string + type: object + trustedCA: + description: TrustedCA defines which CA certificates are trusted. + properties: + certificates: + description: Certificates defines the list of secretRefs containing trusted CA certificates. + items: + properties: + secretRef: + description: SecretRef defines the reference to a secret containing one or more CA certificates under the key 'ca.crt'. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + required: + - secretRef + type: object + minItems: 1 + type: array + verificationDepth: + default: 1 + description: |- + VerificationDepth specifies the hops in the certificate chain at which validation is performed. + 1 means that either the leaf or the signing CA must be in the set of trusted certificates. + format: int32 + type: integer + required: + - certificates + type: object + type: object + disabled: + description: |- + Disabled specifies to trust any certificate without verification. + THIS IS INSECURE AND SHOULD ONLY BE USED FOR TESTING. + type: object + publicCAs: + description: PublicCAs specifies to only accept certificates with a SAN matching "uri" and which are signed by a CA which is either directly or indirectly trusted by any of the root CA certificates shipped with the Airlock Microgateway Engine's base image. + type: object + type: object + ciphers: + description: Ciphers defines a list of the supported TLS cipher suites. For details on cipher list refer to the envoy documentation on cipher_suites in common tls configuration. + items: + type: string + minItems: 1 + type: array + protocol: + description: Protocol defines the supported TLS protocol versions. + properties: + maximum: + description: Maximum supported TLS version. + enum: + - TLSv1_0 + - TLSv1_1 + - TLSv1_2 + - TLSv1_3 + type: string + minimum: + description: Minimum supported TLS version. + enum: + - TLSv1_0 + - TLSv1_1 + - TLSv1_2 + - TLSv1_3 + type: string + type: object + type: object + uri: + description: URI specifies the endpoint address. + format: uri + minLength: 1 + pattern: ^(http|https)://.*$ + type: string + required: + - uri + type: object + required: + - authorization + - token + type: object + issuer: + description: Issuer specifies the unique identifier of the OIDC Provider, which is used e.g. for signature verification. + format: uri + minLength: 1 + pattern: ^(http|https)://.*$ + type: string + tokenValidation: + description: TokenValidation configures token validation. + properties: + idToken: + description: IDToken configures validation for the OIDC ID Token. + properties: + signatureVerification: + description: SignatureVerification specifies how to verify the ID Token signature. + properties: + disabled: + description: Disabled specifies to skip verification of the JWT signature. Not recommended for production environments. + type: object + jwksRef: + description: JwksRef specifies the JWKS to use for verifying the JWT signature (usually provided by the OpenID Provider). + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + type: object + required: + - signatureVerification + type: object + required: + - idToken + type: object + required: + - endpoints + - issuer + - tokenValidation + type: object + type: object + required: + - spec + type: object + served: true + storage: true + subresources: {} diff --git a/charts/airlock/microgateway/4.4.0/crds/oidcrelyingparties.microgateway.airlock.com.yaml b/charts/airlock/microgateway/4.4.0/crds/oidcrelyingparties.microgateway.airlock.com.yaml new file mode 100644 index 000000000..3c071bc66 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/crds/oidcrelyingparties.microgateway.airlock.com.yaml @@ -0,0 +1,235 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + labels: + app.kubernetes.io/name: airlock-microgateway-operator + app.kubernetes.io/version: 4.4.0 + name: oidcrelyingparties.microgateway.airlock.com +spec: + group: microgateway.airlock.com + names: + categories: + - airlock-microgateway + kind: OIDCRelyingParty + listKind: OIDCRelyingPartyList + plural: oidcrelyingparties + singular: oidcrelyingparty + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + OIDCRelyingParty specifies how the Airlock Microgateway Engine interacts with an OpenID Provider (OP). + + {{% notice info %}} The OIDC feature requires SessionHandling to be configured in the SidecarGateway. {{% /notice %}} + 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: Specification of the OIDC Relying Party configuration. + properties: + clientID: + description: ClientID specifies the OIDCRelyingParty "client_id". + minLength: 1 + type: string + credentials: + description: Credentials used for client authentication on the back-channel with the authorization server. + properties: + clientSecret: + description: ClientSecret authenticates with the client password issued by the OpenID Provider (OP). + properties: + method: + default: BasicAuth + description: Method specifies in which format the client secret is sent with the authorization request. + enum: + - BasicAuth + - FormURLEncoded + type: string + secretRef: + description: SecretRef specifies the kubernetes secret containing the client password with key "client.secret". + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + required: + - secretRef + type: object + required: + - clientSecret + type: object + flowTimeout: + default: 5m + description: FlowTimeout specifies the time window within which an initiated OIDC flow can be completed by the client. + type: string + oidcProviderRef: + description: OIDCProviderRef selects the OpenID Provider (OP) used to authenticate users. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + pathMapping: + description: PathMapping configures the action matching. + properties: + logoutPath: + description: |- + LogoutPath specifies which request paths should initiate a logout. + + WARNING: If the AccessControl policy referencing this OIDCRelyingParty has a request condition, you must currently ensure that it also matches these logout requests. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + redirectPath: + description: |- + RedirectPath specifies which request paths should be interpreted as a callback redirect from the authorization endpoint. + + WARNING: If the AccessControl policy referencing this OIDCRelyingParty has a request condition, you must currently ensure that it also matches these callback redirect requests. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + required: + - logoutPath + - redirectPath + type: object + redirectURI: + description: |- + RedirectURI configures the "redirect_uri" parameter included in the authorization request. + May contain envoy command operators, e.g.: `%REQ(:x-forwarded-proto)%://%REQ(:authority)%/callback` + + WARNING: If the AccessControl policy referencing this OIDCRelyingParty has a request condition, you must currently + ensure that it also matches requests to this URI. + minLength: 1 + type: string + scopes: + description: |- + Scopes specifies the scopes to request during the OIDC flow. + The mandatory `openid` scope is implicitly added to the list if not already present. + Default: `['openid', 'profile']` + + Note: Different OIDCRelyingParties which use the same OIDC Provider and Client ID must request the same scopes for now. + items: + minLength: 1 + type: string + type: array + required: + - clientID + - credentials + - oidcProviderRef + - pathMapping + - redirectURI + type: object + required: + - spec + type: object + served: true + storage: true + subresources: {} diff --git a/charts/airlock/microgateway/4.4.0/crds/openapis.microgateway.airlock.com.yaml b/charts/airlock/microgateway/4.4.0/crds/openapis.microgateway.airlock.com.yaml new file mode 100644 index 000000000..f4b9bc285 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/crds/openapis.microgateway.airlock.com.yaml @@ -0,0 +1,167 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + labels: + app.kubernetes.io/name: airlock-microgateway-operator + app.kubernetes.io/version: 4.4.0 + name: openapis.microgateway.airlock.com +spec: + group: microgateway.airlock.com + names: + categories: + - airlock-microgateway + kind: OpenAPI + listKind: OpenAPIList + plural: openapis + singular: openapi + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: OpenAPI contains the configuration for the OpenAPI specification. + 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: Specification of the desired OpenAPI specification. + properties: + response: + description: Response defines the validation behaviour for responses. + properties: + secured: + description: Secured enables response checking. + properties: + validation: + default: Lax + description: Validation defines the validation mode for responses. + enum: + - Lax + - Strict + type: string + type: object + unsecured: + description: Unsecured disables response checking. + type: object + type: object + settings: + description: Settings defines the settings to configure OpenAPI specification enforcement. + properties: + logging: + description: Logging specifies the access log behavior. + properties: + maxFailedSubvalidations: + default: 10 + description: MaxFailedSubvalidations defines the maximum number of failed subvalidations being logged. + format: int64 + type: integer + type: object + schema: + description: Schema configures the OpenAPI specification. + properties: + source: + description: Source specifies the OpenAPI specification to be enforced. + properties: + configMapRef: + description: ConfigMapRef references the configmap by its name containing the well-known key 'openapi.json'. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + type: object + required: + - source + type: object + threatHandlingMode: + default: Block + description: ThreatHandlingMode specifies how threats should be handled. + enum: + - Block + - LogOnly + type: string + validation: + description: Validation specifies the patterns for the validation behavior. + properties: + authentication: + description: Authentication defines the settings for the authentication scheme. + properties: + oAuth2: + description: OAuth2 specifies the OAuth2 parameters. + properties: + allowedParameters: + description: AllowedParameters specifies the allowed parameters for the authentication scheme. + properties: + builtIn: + description: BuiltIn allows configuring a set of predefined allowed parameters. + properties: + standardParameters: + default: true + description: StandardParameters defines whether the allowed parameters should be expanded by the set of common parameters. + type: boolean + type: object + custom: + description: Custom allows configuring additional allowed parameters. + items: + minLength: 1 + type: string + minItems: 1 + type: array + type: object + type: object + oidc: + description: Oidc specifies the OIDC parameters. + properties: + allowedParameters: + description: AllowedParameters specifies the allowed parameters for the authentication scheme. + properties: + builtIn: + description: BuiltIn allows configuring a set of predefined allowed parameters. + properties: + standardParameters: + default: true + description: StandardParameters defines whether the allowed parameters should be expanded by the set of common parameters. + type: boolean + type: object + custom: + description: Custom allows configuring additional allowed parameters. + items: + minLength: 1 + type: string + minItems: 1 + type: array + type: object + type: object + type: object + type: object + required: + - schema + type: object + required: + - settings + type: object + required: + - spec + type: object + served: true + storage: true diff --git a/charts/airlock/microgateway/4.4.0/crds/parsers.microgateway.airlock.com.yaml b/charts/airlock/microgateway/4.4.0/crds/parsers.microgateway.airlock.com.yaml new file mode 100644 index 000000000..ab0a5f4d2 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/crds/parsers.microgateway.airlock.com.yaml @@ -0,0 +1,358 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + labels: + app.kubernetes.io/name: airlock-microgateway-operator + app.kubernetes.io/version: 4.4.0 + name: parsers.microgateway.airlock.com +spec: + group: microgateway.airlock.com + names: + categories: + - airlock-microgateway + kind: Parser + listKind: ParserList + plural: parsers + singular: parser + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Parser contains the configuration for content parsers (default and custom). + 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: Specification of the desired parser behavior. + properties: + request: + description: Request defines the parsing for downstream requests. + properties: + custom: + description: Custom allows configuring additional rules for parser selection. + properties: + rules: + description: |- + Rules defines a custom set prepended before built-in rules of enabled request parsers. + Disable all built-in parsers to overrule them completely. + items: + properties: + action: + description: |- + Action specifies what should happen when a request condition matches. + Only one of parse or skip can be set. + properties: + parse: + description: Parse activates the configured parser. + properties: + form: + description: Form activates the Form parser. + type: object + json: + description: JSON activates the JSON parser. + type: object + multipart: + description: Multipart activates the multipart parser. + type: object + type: object + skip: + description: Skip disables any content parsing + type: object + type: object + requestConditions: + description: RequestConditions defines additional request properties which must be matched in order for this rule to apply. + properties: + header: + description: Header defines the matching headers of a request. + properties: + name: + description: Name defines the name of a header. + properties: + matcher: + description: Matcher defines the way to match a string. In comparison to a normal StringMatcher, a value is always matched ignoring the case and can't be inverted. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + value: + description: Value defines the value of a header. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + type: object + invert: + default: false + description: Invert indicates whether the request condition should be inverted. + type: boolean + mediaType: + description: MediaType defines the matching media type from the content-type header of a request. + properties: + matcher: + description: |- + NonInvertableCaseInsensitiveStringMatcher defines the way to match a string. + In comparison to a normal StringMatcher, a value is always matched ignoring the case and can't be inverted. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + method: + description: Method defines the matching methods of a request. + items: + description: Method defines common HTTP methods. + enum: + - GET + - HEAD + - POST + - PUT + - PATCH + - DELETE + - CONNECT + - OPTIONS + - TRACE + type: string + type: array + path: + description: Path defines the matching path of a request. + properties: + matcher: + description: StringMatcher defines the way to match a string. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + required: + - matcher + type: object + remoteIP: + description: RemoteIP defines the matching remote IPs of a request. + properties: + cidrRanges: + description: CIDRRanges defines the IPv4 or IPv6 CIDR ranges, e.g. ``196.148.3.128/26`` or ``2001:db8::/28``. + items: + description: CIDRRange defines an IPv4 or IPv6 CIDR range, e.g. “196.148.3.128/26“ or “2001:db8::/28“. + format: cidr + type: string + minItems: 1 + type: array + invert: + default: false + description: Invert indicates whether the match should be inverted. + type: boolean + required: + - cidrRanges + type: object + type: object + required: + - action + - requestConditions + type: object + type: array + type: object + defaultContentType: + default: application/x-www-form-urlencoded + description: DefaultContentType specifies the content-type header which should be injected into the request before parser selection if it is not already present and the request has a body. + minLength: 1 + type: string + parsers: + description: Parsers defines the configuration for the available content parsers. + properties: + form: + description: Form defines the configuration for the form parser. + properties: + enable: + default: true + description: Enable defines whether form payloads are inspected. + type: boolean + mediaTypePattern: + default: .*urlencoded.* + description: MediaTypePattern is a regex specifying the media types for which the request body should be treated as form arguments. + minLength: 1 + type: string + type: object + json: + description: JSON defines the configuration for the JSON parser. + properties: + enable: + default: true + description: Enable defines whether json payloads are inspected. + type: boolean + mediaTypePattern: + default: .*json.* + description: MediaTypePattern is a regex specifying the media types for which the request body should be treated as JSON. + minLength: 1 + type: string + type: object + multipart: + description: Multipart defines the configuration for the multipart parser. + properties: + enable: + default: true + description: Enable defines whether multipart payloads are inspected. + type: boolean + mediaTypePattern: + default: .*multipart.* + description: MediaTypePattern is a regex specifying the media types for which the request body should be treated as a multipart payload. + minLength: 1 + type: string + type: object + type: object + type: object + type: object + type: object + served: true + storage: true diff --git a/charts/airlock/microgateway/4.4.0/crds/redisproviders.microgateway.airlock.com.yaml b/charts/airlock/microgateway/4.4.0/crds/redisproviders.microgateway.airlock.com.yaml new file mode 100644 index 000000000..44b70be9b --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/crds/redisproviders.microgateway.airlock.com.yaml @@ -0,0 +1,232 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + labels: + app.kubernetes.io/name: airlock-microgateway-operator + app.kubernetes.io/version: 4.4.0 + name: redisproviders.microgateway.airlock.com +spec: + group: microgateway.airlock.com + names: + categories: + - airlock-microgateway + kind: RedisProvider + listKind: RedisProviderList + plural: redisproviders + singular: redisprovider + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: RedisProvider contains a client configuration for connecting to a Redis database. + 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: Specification of a Redis database client configuration. + properties: + auth: + description: Auth specifies the Redis credentials. + properties: + password: + description: Password specifies the Redis password. + properties: + secretRef: + description: SecretRef selects the secret containing the Redis password under the key 'redis.password'. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + required: + - secretRef + type: object + username: + default: default + description: Username specifies the Redis username to authenticate with. + minLength: 1 + pattern: ^[^\s]+$ + type: string + required: + - password + type: object + mode: + description: Mode configures the redis deployment mode. + properties: + cluster: + description: Cluster specifies the Redis Cluster to connect to. + properties: + nodes: + description: Nodes specifies the Cluster nodes. + items: + properties: + host: + description: Host specifies the IP or hostname. + minLength: 1 + pattern: ^(\d{1,3}(\.\d{1,3}){3}|([0-9a-fA-F]{1,4}|:)+(:\d{1,3}(\.\d{1,3}){3})?|[a-z0-9\-]+(\.[a-z0-9\-]+)*)$ + type: string + port: + default: 6379 + description: Port specifies the port. + maximum: 65535 + minimum: 1 + type: integer + required: + - host + type: object + minItems: 1 + type: array + required: + - nodes + type: object + sentinel: + description: Sentinel specifies the Redis Sentinels to connect to. + properties: + masterName: + description: MasterName specifies the master name. + minLength: 1 + type: string + nodes: + description: Nodes specifies the Sentinel nodes. + items: + properties: + host: + description: Host specifies the IP or hostname. + minLength: 1 + pattern: ^(\d{1,3}(\.\d{1,3}){3}|([0-9a-fA-F]{1,4}|:)+(:\d{1,3}(\.\d{1,3}){3})?|[a-z0-9\-]+(\.[a-z0-9\-]+)*)$ + type: string + port: + default: 6379 + description: Port specifies the port. + maximum: 65535 + minimum: 1 + type: integer + required: + - host + type: object + minItems: 1 + type: array + required: + - masterName + - nodes + type: object + standalone: + description: Standalone specifies the standalone Redis instance to connect to. + properties: + host: + description: Host specifies the IP or hostname. + minLength: 1 + pattern: ^(\d{1,3}(\.\d{1,3}){3}|([0-9a-fA-F]{1,4}|:)+(:\d{1,3}(\.\d{1,3}){3})?|[a-z0-9\-]+(\.[a-z0-9\-]+)*)$ + type: string + port: + default: 6379 + description: Port specifies the port. + maximum: 65535 + minimum: 1 + type: integer + required: + - host + type: object + type: object + timeouts: + description: Timeouts specifies the timeouts when interacting with the Redis endpoint. + properties: + connect: + default: 5s + description: Connect specifies the timeout for establishing a connection. + type: string + maxDuration: + default: 2s + description: MaxDuration specifies the response timeout. + type: string + type: object + tls: + description: TLS defines TLS settings. If not specified, TLS is disabled i.e. unencrypted TCP is used when connecting to the Redis instance. + properties: + certificateVerification: + description: CertificateVerification specifies how the certificate presented by the server is verified. + properties: + custom: + description: Custom explicitly specifies how the server certificate should be verified. + properties: + trustedCA: + description: TrustedCA defines which CA certificates are trusted. + properties: + certificates: + description: Certificates defines the list of secretRefs containing trusted CA certificates. + items: + properties: + secretRef: + description: SecretRef defines the reference to a secret containing one or more CA certificates under the key 'ca.crt'. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + required: + - secretRef + type: object + minItems: 1 + type: array + required: + - certificates + type: object + required: + - trustedCA + type: object + disabled: + description: 'Disabled specifies to trust any certificate without verification. THIS IS INSECURE AND SHOULD ONLY BE USED FOR TESTING. Note: This setting currently also disables TLS SNI.' + type: object + publicCAs: + description: PublicCAs specifies to only accept certificates with a SAN matching the host and which are signed by a CA which is either directly or indirectly trusted by any of the root CA certificates shipped with the Airlock Microgateway Session Agent’s base image. + type: object + type: object + clientCertificate: + description: ClientCertificate configures client certificate authentication. If not specified, TLS-based client authentication is disabled. + properties: + secretRef: + description: SecretRef specifies the client certificate to use (secret of type kubernetes.io/tls). + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + required: + - secretRef + type: object + type: object + required: + - mode + type: object + required: + - spec + type: object + served: true + storage: true diff --git a/charts/airlock/microgateway/4.4.0/crds/sessionhandlings.microgateway.airlock.com.yaml b/charts/airlock/microgateway/4.4.0/crds/sessionhandlings.microgateway.airlock.com.yaml new file mode 100644 index 000000000..401427514 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/crds/sessionhandlings.microgateway.airlock.com.yaml @@ -0,0 +1,89 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + labels: + app.kubernetes.io/name: airlock-microgateway-operator + app.kubernetes.io/version: 4.4.0 + name: sessionhandlings.microgateway.airlock.com +spec: + group: microgateway.airlock.com + names: + categories: + - airlock-microgateway + kind: SessionHandling + listKind: SessionHandlingList + plural: sessionhandlings + singular: sessionhandling + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: SessionHandling contains the configuration for session handling. + 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: Specification of the desired session handling behavior. + properties: + defaultTimeouts: + description: DefaultTimeouts specifies the session timeouts to apply when not provided by the authentication method. + properties: + lifetime: + default: 12h + description: Lifetime specifies the maximum duration a session can exist. + type: string + type: object + persistence: + description: Persistence configures where to store the session state. + properties: + redisProviderRef: + description: RedisProviderRef specifies to cache session information in the provided Redis instance. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + required: + - redisProviderRef + type: object + prefix: + description: |- + Prefix specifies the prefix under which the sessions should be stored in the persistence layer. + If not specified, an automatic prefix derived from the namespaced SessionHandling CR name is used, which ensures that sessions will always be isolated on Microgateways configured with different SessionHandling CRs, even if they share the same persistence backend. + + To allow session sharing between different Microgateway deployments, ensure that the prefix and persistence backend is the same across all corresponding SessionHandling CRs. + + Note: Session cookies are currently never shared across different fully qualified domain names (FQDNs) and authentication via different OIDC Relying Parties generates different session cookies. Clients will therefore only able to transparently reuse session cookies for connecting to different Microgateway deployments if those are a) exposed under the same FQDN and b) handle authentication via the same OIDC Relying Party. + maxLength: 64 + minLength: 1 + pattern: ^[a-zA-Z][a-zA-Z0-9_]*$ + type: string + required: + - persistence + type: object + required: + - spec + type: object + served: true + storage: true diff --git a/charts/airlock/microgateway/4.4.0/crds/sidecargateways.microgateway.airlock.com.yaml b/charts/airlock/microgateway/4.4.0/crds/sidecargateways.microgateway.airlock.com.yaml new file mode 100644 index 000000000..d4de013f6 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/crds/sidecargateways.microgateway.airlock.com.yaml @@ -0,0 +1,758 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + labels: + app.kubernetes.io/name: airlock-microgateway-operator + app.kubernetes.io/version: 4.4.0 + name: sidecargateways.microgateway.airlock.com +spec: + group: microgateway.airlock.com + names: + categories: + - airlock-microgateway + kind: SidecarGateway + listKind: SidecarGatewayList + plural: sidecargateways + singular: sidecargateway + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: SidecarGateway contains the configuration how to configure the Airlock Microgateway Engine when used as Sidecar Container within the Pod of an application. + 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: Specification of the desired sidecar gateway behavior. + properties: + applications: + description: Applications defines applications which run on different ports. + items: + properties: + containerPort: + default: 8080 + description: |- + ContainerPort refers to the container port. + This must be a valid port number, 0 < x < 65536. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + downstream: + description: Downstream defines the downstream configuration for this application + properties: + protocol: + description: |- + Protocol defines the exposed HTTP protocol version. At most one of http1, http2 and auto can be set. + Default: auto: {} + properties: + auto: + description: Auto specifies that the protocol should be inferred. + properties: + http2: + description: HTTP2 specifies the settings for when HTTP/2 is inferred. + properties: + allowConnect: + default: false + description: Allows proxying Websocket and other upgrades over H2 connect. + type: boolean + type: object + type: object + http1: + description: HTTP1 specifies that the client is assumed to speak HTTP/1.1. + type: object + http2: + description: HTTP2 specifies that the client is assumed to speak HTTP/2. + properties: + allowConnect: + default: false + description: Allows proxying Websocket and other upgrades over H2 connect. + type: boolean + type: object + type: object + remoteIP: + description: |- + RemoteIP defines how the remote IP of a client is propagated. + Default: xff: {...} + properties: + connectionIP: + description: ConnectionIP configures to use the source IP address of the direct downstream connection. + type: object + customHeader: + description: CustomHeader specifies to use a custom header for remote IP extraction. + properties: + headerName: + description: HeaderName specifies the name of the custom header containing the remote IP. + minLength: 1 + type: string + required: + default: true + description: Required specifies if the custom header is required. If true and not available the request will be rejected with 403. + type: boolean + required: + - headerName + type: object + xff: + description: XFF configures to use the standard 'X-Forwarded-For' header for IP extraction. + properties: + numTrustedHops: + default: 1 + description: NumTrustedHops specifies to extract the client's originating IP from the nth rightmost entry in the X-Forwarded-For header. With the default value of 1, the IP is extracted from the rightmost entry. + format: int32 + minimum: 1 + type: integer + type: object + type: object + requestNormalizations: + description: RequestNormalizations defines a set of normalization actions which are applied to the request before route matching. + properties: + mergeSlashes: + default: true + description: MergeSlashes ensures that adjacent slashes in the path are merged into one. + type: boolean + normalizePath: + default: true + description: NormalizePath ensures normalization according to RFC 3986 without case normalization. + type: boolean + type: object + restrictions: + description: Restrictions defines restrictions for downstream. + properties: + http: + description: HTTP defines limits for the HTTP protocol. + properties: + headersLength: + anyOf: + - type: integer + - type: string + default: 60Ki + description: HeadersLength defines maximum size of all request headers combined. Requests that exceed this limit will receive a 431 response. + 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 + type: object + timeouts: + description: Timeouts defines timeouts for downstream + properties: + http: + description: HTTP defines the settings for HTTP timeouts. + properties: + idle: + default: 5m + description: |- + Idle defines the settings for the idle timeout when no data is sent or received. + A value of 0 will completely disable the timeout. + Default: 5m + type: string + maxDuration: + default: 5m + description: |- + MaxDuration defines the total duration for a HTTP request/response stream. + A value of 0 will completely disable the timeout. + Default: 5m + type: string + requestHeaders: + default: 10s + description: |- + RequestHeaders defines the duration before all request headers must be received. + A value of 0 will completely disable the timeout. + Default: 10s + type: string + type: object + type: object + tls: + description: TLS defines the TLS settings. + properties: + ciphers: + description: Ciphers defines a list of the supported TLS cipher suites. For details on cipher list refer to the envoy documentation on cipher_suites in common tls configuration. + items: + type: string + minItems: 1 + type: array + clientCertificate: + description: |- + ClientCertificate defines the TLS settings for verification of client certificates. + At most one of ignored, optional and required can be set. + Default: ignored: {} + properties: + ignored: + description: Ignored disables verification of the client certificate. + type: object + optional: + description: |- + Optional enables verification of the client certificate if one is presented. + In this mode only trustedCA and crl settings can be configured since certificatePinning and allowedSANs require a client certificate. + properties: + crl: + description: CRL defines the Certificate Revocation List (CRL) settings. + properties: + lists: + description: Lists defines the list of secretRefs containing Certificate Revocation Lists. + items: + properties: + secretRef: + description: SecretRef defines the reference to a secret containing one or more CRL's (in PEM format) under the key 'ca.crl'. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + required: + - secretRef + type: object + minItems: 1 + type: array + validationMode: + default: VerifyChain + description: ValidationMode defines whether only the leaf certificate or also the CA certs should be checked. + enum: + - VerifyLeafCertOnly + - VerifyChain + type: string + type: object + trustedCA: + description: TrustedCA defines which CA certificates are trusted. + properties: + certificates: + description: Certificates defines the list of secretRefs containing trusted CA certificates. + items: + properties: + secretRef: + description: SecretRef defines the reference to a secret containing one or more CA certificates under the key 'ca.crt'. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + required: + - secretRef + type: object + minItems: 1 + type: array + verificationDepth: + default: 1 + description: |- + VerificationDepth specifies the hops in the certificate chain at which validation is performed. + 1 means that either the leaf or the signing CA must be in the set of trusted certificates. + format: int32 + type: integer + required: + - certificates + type: object + required: + - trustedCA + type: object + required: + description: |- + Required contains settings for client certificate verification. A client must present a valid certificate. + At least one of trustedCA and certificatePinning must be set. + properties: + allowedSANs: + description: |- + AllowedSANs is a list of matchers to verify the Subject Alternative name. If specified, it will verify that the + Subject Alternative Name of the presented certificate matches one of the specified matchers. The matching uses “any” semantics, + that is to say, the SAN is verified if at least one matcher is matched. + AllowedSANs requires trustedCA to be set. + items: + description: |- + TLSValidationContextSANMatcher is a list of matchers to verify the Subject Alternative name. If specified, it will verify that the + Subject Alternative Name of the presented certificate matches one of the specified matchers. + properties: + matcher: + description: Matcher defines the string matcher for the SAN value. + properties: + contains: + description: |- + Contains defines a substring match on the substring specified here. Empty contains match is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + exact: + description: |- + Exact defines an explicit match on the string specified here. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + ignoreCase: + default: false + description: IgnoreCase indicates whether the matching should be case-insensitive. In case of a regex match, the regex gets wrapped with a group `(?i:...)`. + type: boolean + prefix: + description: |- + Prefix defines a prefix match on the prefix specified here. Empty prefix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + regex: + description: |- + Regex defines a regex match on the regular expression specified here. Google's [RE2 regex engine](https://github.com/google/re2/wiki/Syntax) is used. + The regex matches only single-line by default, even with ".*". To match a multi-line string prepend (?s) to your regex. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + suffix: + description: |- + Suffix defines a suffix match on the suffix specified here. Empty suffix is not allowed, please use regex instead. + Only one of exact, prefix, suffix, regex or contains can be set. + minLength: 1 + type: string + type: object + sanType: + description: SanType defines the type of SAN matcher. + enum: + - DNS + - Email + - URI + - IPAddress + type: string + required: + - matcher + - sanType + type: object + minItems: 1 + type: array + certificatePinning: + description: |- + CertificatePinning defines the constraints a client certificate must fulfill. + If more than one constraint is configured only one must be satisfied. + At least one of allowedSPKIs and allowedHashes must be set. + properties: + allowedHashes: + description: |- + AllowedHashes is a list of hex-encoded SHA-256 hashes. + If specified, it will verify that the SHA-256 of the DER-encoded presented certificate matches one of the specified values. + items: + type: string + minItems: 1 + type: array + allowedSPKIs: + description: |- + AllowedSPKIs is a list of base64-encoded SHA-256 hashes. + If specified, it will verify that the SHA-256 of the DER-encoded Subject Public Key Information (SPKI) of the presented certificate matches one of the specified values. + items: + type: string + minItems: 1 + type: array + type: object + crl: + description: CRL defines the Certificate Revocation List (CRL) settings. + properties: + lists: + description: Lists defines the list of secretRefs containing Certificate Revocation Lists. + items: + properties: + secretRef: + description: SecretRef defines the reference to a secret containing one or more CRL's (in PEM format) under the key 'ca.crl'. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + required: + - secretRef + type: object + minItems: 1 + type: array + validationMode: + default: VerifyChain + description: ValidationMode defines whether only the leaf certificate or also the CA certs should be checked. + enum: + - VerifyLeafCertOnly + - VerifyChain + type: string + type: object + trustedCA: + description: TrustedCA defines which CA certificates are trusted. + properties: + certificates: + description: Certificates defines the list of secretRefs containing trusted CA certificates. + items: + properties: + secretRef: + description: SecretRef defines the reference to a secret containing one or more CA certificates under the key 'ca.crt'. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + required: + - secretRef + type: object + minItems: 1 + type: array + verificationDepth: + default: 1 + description: |- + VerificationDepth specifies the hops in the certificate chain at which validation is performed. + 1 means that either the leaf or the signing CA must be in the set of trusted certificates. + format: int32 + type: integer + required: + - certificates + type: object + type: object + type: object + enable: + default: false + description: Enable defines if the downstream connection is encrypted. + type: boolean + protocol: + description: Protocol defines the supported TLS protocol versions. + properties: + maximum: + description: Maximum supported TLS version. + enum: + - TLSv1_0 + - TLSv1_1 + - TLSv1_2 + - TLSv1_3 + type: string + minimum: + description: Minimum supported TLS version. + enum: + - TLSv1_0 + - TLSv1_1 + - TLSv1_2 + - TLSv1_3 + type: string + type: object + secretRef: + description: SecretRef defines the reference to the TLS server certificate (secret of type kubernetes.io/tls). + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + xfcc: + description: |- + XFCC defines the handling of X-Forwarded-Client-Cert header. Meaning of the possible values: + _Sanitize_: Do not send the XFCC header to the next hop. This is the default value. + _ForwardOnly_: When the client connection is mTLS (Mutual TLS), forward the XFCC header in the request. + _AppendAndForward_: When the client connection is mTLS, append the client certificate information to the request’s XFCC header and forward it. + _SanitizeAndSet_: When the client connection is mTLS, reset the XFCC header with the client certificate information and send it to the next hop. + _AlwaysForwardOnly_: Always forward the XFCC header in the request, regardless of whether the client connection is mTLS. + Note: When forwarding the XFCC header in the request you might have to adjust the header length restrictions (See sidecargateway.spec.applications.downstream.restrictions.http) + enum: + - Sanitize + - ForwardOnly + - AppendAndForward + - SanitizeAndSet + - AlwaysForwardOnly + type: string + type: object + type: object + envoyHTTPFilterRefs: + description: EnvoyHTTPFilterRefs selects the relevant EnvoyHTTPFilters. + properties: + prepend: + description: Prepend selects the relevant EnvoyHTTPFilters which are added before those configured by the Airlock Microgateway. + items: + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + type: array + type: object + routes: + description: Routes defines the security configurations for different paths. The first matching route (from top to bottom) applies. + items: + description: |- + SidecarGatewayApplicationRoute defines the security configurations for different paths. + At most one of secured and unsecured can be set. + Default: secured: {...} + properties: + pathPrefix: + default: / + description: PathPrefix defines the path prefix used during route selection. + minLength: 1 + type: string + secured: + description: Secured enables WAF processing for this route. + properties: + accessControlRef: + description: |- + AccessControlRef selects the relevant AccessControl configuration resource. + If undefined, Airlock Microgateway does not perform any access control. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + contentSecurityRef: + description: |- + ContentSecurityRef selects the relevant ContentSecurity configuration resource. + If undefined, default settings are applied, designed to work with most upstream web application services. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + type: object + unsecured: + description: |- + Unsecured disables all WAF functionality and therefore protection for this route. + WARNING: Using this setting when the application is exposed to untrusted downstream traffic is highly discouraged. + type: object + type: object + type: array + x-kubernetes-list-map-keys: + - pathPrefix + x-kubernetes-list-type: map + telemetryRef: + description: |- + TelemetryRef selects the relevant Telemetry configuration resource. + If undefined, default settings are applied, designed to work with most upstream web application services. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + upstream: + description: Upstream defines the upstream configuration for this application + properties: + protocol: + description: |- + Protocol defines HTTP protocol version used to communicate with the upstream. At most one of http1, http2 and auto can be set. + Default: auto: {} + properties: + auto: + description: Auto specifies to negotiate the protocol with TLS ALPN (if TLS is enabled) or, as a fallback, use the same protocol that is used by the downstream connection. + properties: + http2: + description: HTTP2 specifies the settings for when HTTP/2 is inferred. + properties: + allowConnect: + default: false + description: Allows proxying Websocket and other upgrades over H2 connect. + type: boolean + type: object + type: object + http1: + description: HTTP1 specifies to use HTTP/1.1. + type: object + http2: + description: HTTP2 specifies to use HTTP/2. + properties: + allowConnect: + default: false + description: Allows proxying Websocket and other upgrades over H2 connect. + type: boolean + type: object + type: object + timeouts: + description: Timeouts defines the timeout settings. + properties: + http: + description: HTTP defines the settings for HTTP timeouts. + properties: + idle: + description: |- + Timeout defines the settings for http timeouts. If this setting is not specified, the value of applications[].downstream.timeouts.http.idle is inherited. + A value of 0 will completely disable the timeout. + type: string + maxDuration: + default: 15s + description: |- + MaxDuration defines the total duration for a HTTP request/response stream. + Default: 15s + type: string + type: object + type: object + tls: + description: TLS defines the TLS settings. + properties: + ciphers: + description: Ciphers defines a list of the supported TLS cipher suites. For details on cipher list refer to the envoy documentation on cipher_suites in common tls configuration. + items: + type: string + minItems: 1 + type: array + enable: + default: false + description: Enable defines if the upstream connection is encrypted. + type: boolean + protocol: + description: Protocol defines the supported TLS protocol versions. + properties: + maximum: + description: Maximum supported TLS version. + enum: + - TLSv1_0 + - TLSv1_1 + - TLSv1_2 + - TLSv1_3 + type: string + minimum: + description: Minimum supported TLS version. + enum: + - TLSv1_0 + - TLSv1_1 + - TLSv1_2 + - TLSv1_3 + type: string + type: object + type: object + type: object + type: object + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - containerPort + x-kubernetes-list-type: map + envoyClusterRefs: + description: EnvoyClusterRefs selects the relevant EnvoyClusters. + items: + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + podSelector: + description: PodSelector defines to which Pods the configuration will be applied to. + properties: + matchLabels: + additionalProperties: + type: string + description: MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels. + type: object + type: object + sessionHandlingRef: + description: SessionHandlingRef selects the SessionHandling configuration to apply. + properties: + name: + description: Name of the resource + minLength: 1 + type: string + required: + - name + type: object + required: + - applications + type: object + status: + description: Most recently observed status of the SidecarGateway which is populated by the system. This data is read-only and may not be up to date. + properties: + conditions: + items: + properties: + lastTransitionTime: + description: 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 SidecarGateway condition. + type: string + required: + - status + - type + type: object + type: array + pods: + items: + properties: + envoyConfig: + description: EnvoyConfig indicates the name of the EnvoyConfig CR for the Pod. + type: string + name: + description: Name indicates the name of a Pod selected by the SidecarGateway. + type: string + sessionAgentSecret: + type: string + required: + - name + type: object + type: array + status: + type: string + unmanagedPods: + items: + properties: + managedBy: + description: ManagedBy indicates the Airlock Microgateway Operator instance which manages this Pod. + type: string + name: + description: Name indicates the name of a Pod selected by the SidecarGateway. + type: string + sessionAgentSecret: + type: string + required: + - name + type: object + type: array + required: + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/charts/airlock/microgateway/4.4.0/crds/telemetries.microgateway.airlock.com.yaml b/charts/airlock/microgateway/4.4.0/crds/telemetries.microgateway.airlock.com.yaml new file mode 100644 index 000000000..7dcfbc1be --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/crds/telemetries.microgateway.airlock.com.yaml @@ -0,0 +1,96 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + labels: + app.kubernetes.io/name: airlock-microgateway-operator + app.kubernetes.io/version: 4.4.0 + name: telemetries.microgateway.airlock.com +spec: + group: microgateway.airlock.com + names: + categories: + - airlock-microgateway + kind: Telemetry + listKind: TelemetryList + plural: telemetries + singular: telemetry + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Telemetry contains the configuration for telemetry (logging, metrics & tracing). + 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: Specification of the desired telemetry behavior. + properties: + correlation: + description: Correlation defines the correlation aspects of Telemetry. + properties: + idSource: + description: IDSource specifies how an external correlation ID should be obtained for a request. If not specified, no correlation ID will be logged. + properties: + header: + description: Header specifies to extract the correlation ID from a request header. If the header is absent from a request, no correlation ID will be logged. + properties: + name: + default: X-Correlation-Id + description: Name of the header (case-insensitive) from which to extract the correlation ID. + minLength: 1 + type: string + type: object + required: + - header + type: object + request: + description: Request defines the request related correlation settings of Telemetry. + properties: + allowDownstreamRequestID: + default: true + description: AllowDownstreamRequestID defines whether trace sampling will consider a provided x-request-id. + type: boolean + alterRequestID: + default: true + description: AlterRequestID defines whether to alter the UUID to reflect the trace sampling decision. If disabled no modification to the UUID will be performed, this may break tracing in the upstream. + type: boolean + type: object + type: object + logging: + description: Logging defines the logging aspects of Telemetry. + properties: + accessLog: + description: AccessLog defines the access log settings of Telemetry. + properties: + format: + description: Format defines the Access Log format of the sidecar. + properties: + json: + description: JSON defines the Access Log format as JSON. + type: object + x-kubernetes-preserve-unknown-fields: true + type: object + type: object + type: object + type: object + type: object + served: true + storage: true diff --git a/charts/airlock/microgateway/4.4.0/dashboards/blockLogs.json b/charts/airlock/microgateway/4.4.0/dashboards/blockLogs.json new file mode 100644 index 000000000..8c96b4f64 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/dashboards/blockLogs.json @@ -0,0 +1,441 @@ +{ + "__inputs": [ + { + "name": "DS_LOKI", + "label": "Loki", + "description": "", + "type": "datasource", + "pluginId": "loki", + "pluginName": "Loki" + }, + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__elements": {}, + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "10.2.0" + }, + { + "type": "datasource", + "id": "loki", + "name": "Loki", + "version": "1.0.0" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Log entries of threats blocked by Airlock Microgateway.\n\nThe dashboard can be filtered by namespace and block type. Column filters on the table allow for an even more granular filtering of the logs.", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": null, + "links": [ + { + "asDropdown": true, + "icon": "external link", + "includeVars": true, + "keepTime": true, + "tags": [ + "airlock-microgateway" + ], + "targetBlank": true, + "title": "Airlock Microgateway", + "tooltip": "", + "type": "dashboards", + "url": "" + } + ], + "panels": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "text", + "mode": "fixed" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "filterable": true, + "inspect": true + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Namespace" + }, + "properties": [ + { + "id": "custom.width", + "value": 221 + }, + { + "id": "custom.filterable" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Timestamp" + }, + "properties": [ + { + "id": "custom.width", + "value": 214 + }, + { + "id": "unit", + "value": "time: YYYY-MM-DD HH:mm:ss.SSS" + }, + { + "id": "custom.filterable" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "HTTP Method" + }, + "properties": [ + { + "id": "custom.width", + "value": 140 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Client IP" + }, + "properties": [ + { + "id": "custom.width", + "value": 138 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Request ID" + }, + "properties": [ + { + "id": "custom.width", + "value": 328 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Block Type" + }, + "properties": [ + { + "id": "custom.width", + "value": 116 + }, + { + "id": "custom.filterable", + "value": false + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Request Size" + }, + "properties": [ + { + "id": "custom.width", + "value": 126 + }, + { + "id": "unit", + "value": "bytes" + }, + { + "id": "custom.align", + "value": "right" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Block Subtype" + }, + "properties": [ + { + "id": "custom.width", + "value": 217 + } + ] + } + ] + }, + "gridPos": { + "h": 27, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "enablePagination": true, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "10.2.0", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "editorMode": "code", + "expr": "{container=\"airlock-microgateway-engine\", namespace=~\"${namespace:regex}\"} |= \"airlock_request_blocked\" |= \"envoy.access\"\n| json http_method=\"http.request.method\", url=\"url.path\", domain=\"url.domain\", request_size=\"http.request.bytes\", client_ip=\"network.forwarded_ip\", request_id=\"http.request.id\", details=\"airlock.actions.block.details\", block_type=\"airlock.actions.block.block_type\", block_subtype=\"airlock.actions.block.block_subtype\"\n| block_type=~\"${blockType:regex}\"", + "hide": false, + "queryType": "range", + "refId": "Blocks" + } + ], + "title": "Blocked Request logs", + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "source": "labels" + } + }, + { + "id": "filterFieldsByName", + "options": { + "byVariable": false, + "include": { + "names": [ + "Time", + "block_subtype", + "block_type", + "client_ip", + "details", + "domain", + "http_method", + "namespace", + "request_id", + "request_size", + "url" + ] + } + } + }, + { + "id": "organize", + "options": { + "excludeByName": { + "Line": true, + "id": true, + "labelTypes": true, + "labels": true, + "tsNs": false + }, + "includeByName": {}, + "indexByName": { + "Time": 0, + "block_subtype": 7, + "block_type": 6, + "client_ip": 9, + "details": 8, + "domain": 2, + "http_method": 3, + "namespace": 1, + "request_id": 10, + "request_size": 5, + "url": 4 + }, + "renameByName": { + "Time": "Timestamp", + "block_subtype": "Block Subtype", + "block_type": "Block Type", + "client_ip": "Client IP", + "details": "Details", + "domain": "URL Domain", + "http_method": "HTTP Method", + "namespace": "Namespace", + "request_id": "Request ID", + "request_size": "Request Size", + "url": "URL Path" + } + } + } + ], + "type": "table" + } + ], + "schemaVersion": 39, + "tags": [ + "airlock-microgateway" + ], + "templating": { + "list": [ + { + "current": {}, + "hide": 2, + "includeAll": false, + "label": "DS_LOKI", + "multi": false, + "name": "DS_LOKI", + "options": [], + "query": "loki", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(microgateway_license_http_rq_total,namespace)", + "hide": 0, + "includeAll": true, + "label": "Application Namespace", + "multi": true, + "name": "namespace", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(microgateway_license_http_rq_total,namespace)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 5, + "type": "query" + }, + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(microgateway_http_downstream_rq_threats_blocked_total,block_type)", + "hide": 0, + "includeAll": true, + "label": "Block Type", + "multi": true, + "name": "blockType", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(microgateway_http_downstream_rq_threats_blocked_total,block_type)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 5, + "type": "query" + }, + { + "current": {}, + "hide": 2, + "includeAll": false, + "label": "DS_PROMETHEUS", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + } + ] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timeRangeUpdatedDuringEditOrView": false, + "timepicker": {}, + "timezone": "browser", + "title": "Airlock Microgateway Threats Block - Logs", + "uid": "adnyzcvwnyadcc", + "version": 3, + "weekStart": "" +} \ No newline at end of file diff --git a/charts/airlock/microgateway/4.4.0/dashboards/blockMetrics.json b/charts/airlock/microgateway/4.4.0/dashboards/blockMetrics.json new file mode 100644 index 000000000..0b98122ef --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/dashboards/blockMetrics.json @@ -0,0 +1,751 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__elements": {}, + "__requires": [ + { + "type": "panel", + "id": "barchart", + "name": "Bar chart", + "version": "" + }, + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "10.2.0" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "timeseries", + "name": "Time series", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Metrics on threats blocked by Airlock Microgateway.\n\nDashboard can be filtered by namespaces as well as block types.", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": null, + "links": [ + { + "asDropdown": true, + "icon": "external link", + "includeVars": true, + "keepTime": true, + "tags": [ + "airlock-microgateway" + ], + "targetBlank": true, + "title": "Airlock Microgateway", + "tooltip": "", + "type": "dashboards", + "url": "" + } + ], + "panels": [ + { + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 6, + "title": "Airlock Microgateway Threats Block - Metrics", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Total number of requests processed by Airlock Microgateway.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "text", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 0, + "y": 1 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.2.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "disableTextWrap": false, + "editorMode": "code", + "exemplar": false, + "expr": "round(sum(increase(microgateway_license_http_rq_total{namespace=~\"${namespace:regex}\", job=~\"${operator_namespace.regex}/.*-engine\"}[$__range])))", + "format": "time_series", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": true, + "legendFormat": "Processed Requests", + "range": false, + "refId": "A", + "useBackend": false + } + ], + "title": "Requests", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Ratio of blocked requests vs. processed requests by Airlock Microgateway.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "text", + "mode": "fixed" + }, + "mappings": [ + { + "options": { + "match": "nan", + "result": { + "index": 0, + "text": "n/a" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 4, + "y": 1 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.2.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "disableTextWrap": false, + "editorMode": "code", + "exemplar": false, + "expr": "sum(increase(microgateway_http_downstream_rq_threats_blocked_total{block_type=~\"${blockType:regex}\", namespace=~\"${namespace:regex}\", job=~\"${operator_namespace.regex}/.*-engine\"}[$__range])) / sum(increase(microgateway_license_http_rq_total{namespace=~\"${namespace:regex}\", job=~\"${operator_namespace.regex}/.*-engine\"}[$__range]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": true, + "legendFormat": "Blocked Requests (%)", + "range": false, + "refId": "A", + "useBackend": false + } + ], + "title": "% Blocked Requests", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Requests per second processed by Airlock Microgateway along with the corresponding block rate.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "blue", + "mode": "fixed" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "left", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "% Blocks" + }, + "properties": [ + { + "id": "custom.axisPlacement", + "value": "right" + }, + { + "id": "unit", + "value": "percentunit" + }, + { + "id": "color", + "value": { + "fixedColor": "orange", + "mode": "fixed" + } + }, + { + "id": "max", + "value": 1 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Requests per second" + }, + "properties": [ + { + "id": "unit", + "value": "short" + }, + { + "id": "custom.fillOpacity", + "value": 25 + } + ] + } + ] + }, + "gridPos": { + "h": 10, + "w": 20, + "x": 0, + "y": 5 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "timezone": [ + "" + ], + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "sum(rate(microgateway_license_http_rq_total{namespace=~\"${namespace:regex}\", job=~\"${operator_namespace.regex}/.*-engine\"}[1m]))", + "instant": false, + "legendFormat": "Requests per second", + "range": true, + "refId": "Requests per Second" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(rate(microgateway_http_downstream_rq_threats_blocked_total{block_type=~\"${blockType:regex}\", namespace=~\"${namespace:regex}\", job=~\"${operator_namespace.regex}/.*-engine\"}[1m])) / sum(rate(microgateway_license_http_rq_total{namespace=~\"${namespace:regex}\", job=~\"${operator_namespace.regex}/.*-engine\"}[1m]))", + "hide": false, + "instant": false, + "legendFormat": "% Blocks", + "range": true, + "refId": "Blocks" + } + ], + "title": "Requests vs. % Blocks", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Blocked threats by block type.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "super-light-orange", + "mode": "fixed" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisGridShow": true, + "axisLabel": "", + "axisPlacement": "auto", + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 0, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 10, + "x": 0, + "y": 15 + }, + "id": 4, + "options": { + "barRadius": 0, + "barWidth": 0.8, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "orientation": "horizontal", + "showValue": "never", + "stacking": "none", + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "asc" + }, + "xField": "block_type", + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "pluginVersion": "10.2.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "round(sum by (block_type) (increase(microgateway_http_downstream_rq_threats_blocked_total{block_type=~\"${blockType:regex}\", namespace=~\"${namespace:regex}\", job=~\"${operator_namespace.regex}/.*-engine\"}[$__range])))", + "format": "time_series", + "instant": true, + "legendFormat": "__auto", + "range": false, + "refId": "A" + } + ], + "title": "Block Type", + "transformations": [ + { + "id": "reduce", + "options": { + "includeTimeField": false, + "labelsToFields": true, + "mode": "seriesToRows", + "reducers": [ + "sum" + ] + } + } + ], + "type": "barchart" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Blocked threats by block subtype, which are subsets of the various block types.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "light-orange", + "mode": "fixed" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 10, + "x": 10, + "y": 15 + }, + "id": 5, + "options": { + "barRadius": 0, + "barWidth": 0.8, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "orientation": "horizontal", + "showValue": "never", + "stacking": "none", + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + }, + "xField": "block_subtype", + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "pluginVersion": "10.2.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "round(sum by (block_subtype) (increase(microgateway_http_downstream_rq_threats_blocked_total{block_type=~\"${blockType:regex}\", namespace=~\"${namespace:regex}\", job=~\"${operator_namespace.regex}/.*-engine\"}[$__range])))", + "instant": true, + "legendFormat": "__auto", + "range": false, + "refId": "A" + } + ], + "title": "Block Subtype", + "transformations": [ + { + "id": "reduce", + "options": { + "labelsToFields": true, + "reducers": [ + "sum" + ] + } + } + ], + "type": "barchart" + } + ], + "refresh": "", + "schemaVersion": 39, + "tags": [ + "airlock-microgateway" + ], + "templating": { + "list": [ + { + "current": {}, + "hide": 2, + "includeAll": false, + "label": "Datasource Prometheus", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": {}, + "hide": 2, + "includeAll": false, + "label": "DS_LOKI", + "multi": false, + "name": "DS_LOKI", + "options": [], + "query": "loki", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(microgateway_license_valid,namespace)", + "hide": 0, + "includeAll": true, + "label": "Operator Namespace", + "multi": true, + "name": "operator_namespace", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(microgateway_license_valid,namespace)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 2, + "regex": ".*", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(microgateway_license_http_rq_total,namespace)", + "hide": 0, + "includeAll": true, + "label": "Application Namespace", + "multi": true, + "name": "namespace", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(microgateway_license_http_rq_total,namespace)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 5, + "type": "query" + }, + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(microgateway_http_downstream_rq_threats_blocked_total,block_type)", + "hide": 0, + "includeAll": true, + "label": "Block Type", + "multi": true, + "name": "blockType", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(microgateway_http_downstream_rq_threats_blocked_total,block_type)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 5, + "type": "query" + } + ] + }, + "time": { + "from": "now-24h", + "to": "now" + }, + "timeRangeUpdatedDuringEditOrView": false, + "timepicker": { + "hidden": false + }, + "timezone": "browser", + "title": "Airlock Microgateway Threats Block - Metrics", + "uid": "ddnqoczu7qvb4cdd3dd", + "version": 3, + "weekStart": "" +} \ No newline at end of file diff --git a/charts/airlock/microgateway/4.4.0/dashboards/headerLogs.json b/charts/airlock/microgateway/4.4.0/dashboards/headerLogs.json new file mode 100644 index 000000000..a6c45008f --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/dashboards/headerLogs.json @@ -0,0 +1,378 @@ +{ + "__inputs": [ + { + "name": "DS_LOKI", + "label": "Loki", + "description": "", + "type": "datasource", + "pluginId": "loki", + "pluginName": "Loki" + } + ], + "__elements": {}, + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "10.2.0" + }, + { + "type": "datasource", + "id": "loki", + "name": "Loki", + "version": "1.0.0" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Logs for header rewrites by Airlock Microgateway, retrieved from corresponding access logs.\n\nThe dashboard can be filtered by namespace. Column filters on the table allow for an even more granular filtering of the logs.", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": null, + "links": [ + { + "asDropdown": true, + "icon": "external link", + "includeVars": true, + "keepTime": true, + "tags": [ + "airlock-microgateway" + ], + "targetBlank": true, + "title": "Airlock Microgateway", + "tooltip": "", + "type": "dashboards", + "url": "" + } + ], + "panels": [ + { + "datasource": { + "default": false, + "type": "loki", + "uid": "${DS_LOKI}" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "text", + "mode": "fixed" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "filterable": true, + "inspect": true + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Namespace" + }, + "properties": [ + { + "id": "custom.width", + "value": 221 + }, + { + "id": "custom.filterable" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Timestamp" + }, + "properties": [ + { + "id": "custom.width", + "value": 214 + }, + { + "id": "unit", + "value": "time: YYYY-MM-DD HH:mm:ss.SSS" + }, + { + "id": "custom.filterable" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "HTTP Method" + }, + "properties": [ + { + "id": "custom.width", + "value": 140 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Client IP" + }, + "properties": [ + { + "id": "custom.width", + "value": 138 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Request ID" + }, + "properties": [ + { + "id": "custom.width", + "value": 328 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Request Size" + }, + "properties": [ + { + "id": "custom.width", + "value": 126 + }, + { + "id": "unit", + "value": "bytes" + }, + { + "id": "custom.align", + "value": "right" + } + ] + } + ] + }, + "gridPos": { + "h": 27, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "enablePagination": true, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "10.2.0", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "editorMode": "code", + "expr": "{container=\"airlock-microgateway-engine\", namespace=~\"${namespace:regex}\"} |= \"header_rewrites\" |= \"envoy.access\"\n| json http_method=\"http.request.method\", url=\"url.path\", domain=\"url.domain\", request_size=\"http.request.bytes\", client_ip=\"network.forwarded_ip\", request_id=\"http.request.id\", header_request_details=\"airlock.actions.header_rewrites.request\", header_response_details=\"airlock.actions.header_rewrites.response\", log_type=\"event.dataset\" | log_type = `envoy.access`", + "hide": false, + "queryType": "range", + "refId": "Header Rewrites" + } + ], + "title": "Header Rewrite Logs", + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "source": "labels" + } + }, + { + "id": "filterFieldsByName", + "options": { + "byVariable": false, + "include": { + "names": [ + "Time", + "client_ip", + "domain", + "header_request_details", + "header_response_details", + "http_method", + "namespace", + "request_id", + "request_size", + "url" + ] + } + } + }, + { + "id": "organize", + "options": { + "excludeByName": { + "Line": true, + "id": true, + "labelTypes": true, + "labels": true, + "tsNs": false + }, + "includeByName": {}, + "indexByName": { + "Time": 0, + "client_ip": 8, + "domain": 2, + "header_request_details": 6, + "header_response_details": 7, + "http_method": 3, + "namespace": 1, + "request_id": 9, + "request_size": 5, + "url": 4 + }, + "renameByName": { + "Time": "Timestamp", + "client_ip": "Client IP", + "details": "Details", + "domain": "URL Domain", + "header_request_details": "Request Header Actions", + "header_response_details": "Response Header Actions", + "http_method": "HTTP Method", + "namespace": "Namespace", + "request_id": "Request ID", + "request_size": "Request Size", + "url": "URL Path" + } + } + } + ], + "type": "table" + } + ], + "schemaVersion": 39, + "tags": [ + "airlock-microgateway" + ], + "templating": { + "list": [ + { + "current": {}, + "hide": 2, + "includeAll": false, + "label": "DS_LOKI", + "multi": false, + "name": "DS_LOKI", + "options": [], + "query": "loki", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(microgateway_license_http_rq_total,namespace)", + "hide": 0, + "includeAll": true, + "label": "Application Namespace", + "multi": true, + "name": "namespace", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(microgateway_license_http_rq_total,namespace)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 5, + "type": "query" + }, + { + "current": {}, + "hide": 2, + "includeAll": false, + "label": "DS_PROMETHEUS", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + } + ] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Airlock Microgateway Header Rewrites - Logs", + "uid": "adnydadenyadcc", + "version": 1, + "weekStart": "" +} \ No newline at end of file diff --git a/charts/airlock/microgateway/4.4.0/dashboards/license.json b/charts/airlock/microgateway/4.4.0/dashboards/license.json new file mode 100644 index 000000000..14886328a --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/dashboards/license.json @@ -0,0 +1,1063 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__elements": {}, + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "10.2.0" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + }, + { + "type": "panel", + "id": "timeseries", + "name": "Time series", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Overview on Airlock Microgateway License attributes and usage.", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": null, + "links": [ + { + "asDropdown": true, + "icon": "external link", + "includeVars": true, + "keepTime": true, + "tags": [ + "airlock-microgateway" + ], + "targetBlank": true, + "title": "Airlock Microgateway", + "tooltip": "", + "type": "dashboards", + "url": "" + } + ], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Aggregated status of the Airlock Microgateway licenses selected in the dashboard filter.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "color": "red", + "index": 1, + "text": "Invalid" + }, + "1": { + "color": "green", + "index": 0, + "text": "Valid" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.2.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "min(microgateway_license_valid * on (service,instance) group_left(id) microgateway_license_info{id=~\"${license_id.regex}\"})", + "instant": true, + "legendFormat": "License Status", + "range": false, + "refId": "Licenses" + } + ], + "title": "License Status", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Next upcoming expiry date over all Airlock Microgateway licenses selected in the dashboard filter.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "text", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "time: L" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 3, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.2.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "min(microgateway_license_expiry_timestamp_seconds * on (service, namespace) group_left(id) microgateway_license_info{id=~\"${license_id.regex}\"})*1000", + "instant": true, + "legendFormat": "Expiry Date (MM/DD/YYYY)", + "range": false, + "refId": "A" + } + ], + "title": "License Expiry Date", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Sum of the number licensed requests over all Airlock Microgateway license selected in the dashboard filter.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "text", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 7, + "y": 0 + }, + "id": 6, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.2.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "sum(topk(1, (microgateway_license_max_rq_count_per_month > 0) * on (service, namespace) group_left(id) microgateway_license_info{id=~\"${license_id.regex}\"}) by (id))", + "instant": true, + "legendFormat": "Licensed Requests", + "range": false, + "refId": "A" + } + ], + "title": "Licensed Requests", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Sum of the estimated number of requests over 30 days based on the last 7 days over all Airlock Microgateway licenses selected in the dashboard filter.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "text", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 5, + "x": 11, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.2.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "(sum((label_replace(increase(microgateway_license_http_rq_total[7d]), \"namespace\", \"$1\", \"job\", \"(.+)/.*\")) * on(namespace) group_left(id) microgateway_license_info{id=~\"${license_id.regex}\"}))/7*30", + "instant": true, + "legendFormat": "Estimated Requests", + "range": false, + "refId": "A" + } + ], + "title": "Requests over 30 days (estimated)", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Number of requests per week processed by Airlock Microgateway.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "blue", + "mode": "fixed" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 24, + "x": 0, + "y": 4 + }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": " sum((label_replace(avg_over_time(increase(microgateway_license_http_rq_total[7d])[2m:30s]), \"namespace\", \"$1\", \"job\", \"(.+)/.*\")) * on(namespace) group_left(id) microgateway_license_info{id=~\"${license_id.regex}\"})", + "hide": false, + "instant": false, + "legendFormat": "# Requests per week", + "range": true, + "refId": "C" + } + ], + "title": "Processed Requests per week", + "type": "timeseries" + }, + { + "datasource": { + "default": false, + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Estimated number of requests over 30 days based on the last 7 days per operator namespace for the Airlock Microgateway licenses selected in the dashboard filter.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "License ID" + }, + "properties": [ + { + "id": "custom.width", + "value": 330 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Requests over 30 days (estimated)" + }, + "properties": [ + { + "id": "unit", + "value": "short" + }, + { + "id": "mappings", + "value": [ + { + "options": { + "match": "null+nan", + "result": { + "index": 0, + "text": "0" + } + }, + "type": "special" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Operator Namespace" + }, + "properties": [ + { + "id": "custom.width", + "value": 307 + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 11, + "x": 0, + "y": 17 + }, + "id": 7, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "frameIndex": 1, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "10.2.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "(sum by (namespace, id) ((label_replace(increase(microgateway_license_http_rq_total[7d]), \"namespace\", \"$1\", \"job\", \"(.+)/.*\")) * on(namespace) group_left(id) microgateway_license_info{id=~\"${license_id.regex}\"}))/7*30", + "format": "table", + "hide": false, + "instant": true, + "legendFormat": "__auto", + "range": false, + "refId": "Est. Usage over 30 days" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "(min by(namespace) (microgateway_build_info{container=\"manager\"})) * on (namespace) group_left(id) microgateway_license_info{id=~\"${license_id.regex}\"}", + "format": "table", + "hide": false, + "instant": true, + "legendFormat": "__auto", + "range": false, + "refId": "Engine License" + } + ], + "title": "Usage by Operator Namespace", + "transformations": [ + { + "id": "merge", + "options": {} + }, + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "Value #Engine License": true, + "Value #Licensed Req": false, + "container": true, + "endpoint": true, + "instance": true, + "job": true, + "namespace": false, + "pod": true, + "service": true, + "version": true + }, + "includeByName": {}, + "indexByName": { + "Time": 0, + "Value": 3, + "id": 2, + "namespace": 1 + }, + "renameByName": { + "Value #Est. Usage over 30 days": "Requests over 30 days (estimated)", + "Value #License Expiry Date": "Expiry Date", + "Value #License Type": "License Type", + "Value #Licensed Req": "Licensed Requests", + "Value #Validity": "Valid", + "id": "License ID", + "namespace": "Operator Namespace" + } + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Metadata for the Airlock Microgateway licenses selected in the dashboard filter.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "License ID" + }, + "properties": [ + { + "id": "custom.width", + "value": 321 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Valid" + }, + "properties": [ + { + "id": "mappings", + "value": [ + { + "options": { + "0": { + "color": "red", + "index": 1, + "text": "Invalid" + }, + "1": { + "color": "green", + "index": 0, + "text": "Valid" + } + }, + "type": "value" + }, + { + "options": { + "match": "null+nan", + "result": { + "color": "red", + "index": 2, + "text": "Invalid" + } + }, + "type": "special" + } + ] + }, + { + "id": "custom.width", + "value": 65 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "License Type" + }, + "properties": [ + { + "id": "mappings", + "value": [ + { + "options": { + "0": { + "index": 1, + "text": "Community" + }, + "1": { + "index": 0, + "text": "Premium" + } + }, + "type": "value" + }, + { + "options": { + "match": "null+nan", + "result": { + "index": 2, + "text": "n/a" + } + }, + "type": "special" + } + ] + }, + { + "id": "custom.width", + "value": 109 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Expiry Date" + }, + "properties": [ + { + "id": "unit", + "value": "time:L" + }, + { + "id": "custom.width", + "value": 130 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Requests over 30 days (estimated)" + }, + "properties": [ + { + "id": "unit", + "value": "short" + }, + { + "id": "mappings", + "value": [ + { + "options": { + "match": "null+nan", + "result": { + "index": 0, + "text": "0" + } + }, + "type": "special" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Licensed Requests" + }, + "properties": [ + { + "id": "unit", + "value": "short" + }, + { + "id": "custom.width", + "value": 160 + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 13, + "x": 11, + "y": 17 + }, + "id": 8, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "frameIndex": 1, + "showHeader": true, + "sortBy": [ + { + "desc": false, + "displayName": "Expiry Date" + } + ] + }, + "pluginVersion": "10.2.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "min by (id) (microgateway_license_valid * on (service, namespace) group_left(id) microgateway_license_info{id=~\"${license_id.regex}\"})", + "format": "table", + "instant": true, + "legendFormat": "__auto", + "range": false, + "refId": "Validity" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "topk(1,microgateway_license_max_rq_count_per_month * on (service, namespace) group_left(id) microgateway_license_info{id=~\"${license_id.regex}\"})by (id)", + "format": "table", + "hide": false, + "instant": true, + "legendFormat": "__auto", + "range": false, + "refId": "Licensed Req" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "min by (id) (microgateway_license_is_premium * on (service, namespace) group_left(id) microgateway_license_info{id=~\"${license_id.regex}\"})", + "format": "table", + "hide": false, + "instant": true, + "legendFormat": "__auto", + "range": false, + "refId": "License Type" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "min by (id) (microgateway_license_expiry_timestamp_seconds * on (service, namespace) group_left(id) microgateway_license_info{id=~\"${license_id.regex}\"})*1000", + "format": "table", + "hide": false, + "instant": true, + "legendFormat": "__auto", + "range": false, + "refId": "License Expiry Date" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "(sum by (id) ((label_replace(increase(microgateway_license_http_rq_total[7d]), \"namespace\", \"$1\", \"job\", \"(.+)/.*\")) * on(namespace) group_left(id) microgateway_license_info{id=~\"${license_id.regex}\"}))/7*30", + "format": "table", + "hide": false, + "instant": true, + "legendFormat": "__auto", + "range": false, + "refId": "Est. Usage over 30 days" + } + ], + "title": "License Overview", + "transformations": [ + { + "id": "merge", + "options": {} + }, + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "Value #Licensed Req": false, + "container": true, + "endpoint": true, + "instance": true, + "job": true, + "namespace": true, + "pod": true, + "service": true + }, + "includeByName": {}, + "indexByName": {}, + "renameByName": { + "Value #Est. Usage over 30 days": "Requests over 30 days (estimated)", + "Value #License Expiry Date": "Expiry Date", + "Value #License Type": "License Type", + "Value #Licensed Req": "Licensed Requests", + "Value #Validity": "Valid", + "id": "License ID", + "namespace": "Operator Namespace" + } + } + }, + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "equal", + "options": { + "value": "" + } + }, + "fieldName": "License ID" + } + ], + "match": "any", + "type": "exclude" + } + } + ], + "type": "table" + } + ], + "refresh": "", + "schemaVersion": 39, + "tags": [ + "airlock-microgateway" + ], + "templating": { + "list": [ + { + "current": {}, + "hide": 2, + "includeAll": false, + "label": "DS_PROMETHEUS", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(microgateway_license_info,id)", + "description": "", + "hide": 0, + "includeAll": true, + "label": "License ID", + "multi": true, + "name": "license_id", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(microgateway_license_info,id)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-7d", + "to": "now" + }, + "timeRangeUpdatedDuringEditOrView": false, + "timepicker": {}, + "timezone": "browser", + "title": "Airlock Microgateway License", + "uid": "cdpq79bzrr01se", + "version": 2, + "weekStart": "" +} \ No newline at end of file diff --git a/charts/airlock/microgateway/4.4.0/dashboards/logOnlyLogs.json b/charts/airlock/microgateway/4.4.0/dashboards/logOnlyLogs.json new file mode 100644 index 000000000..6d7ae7f22 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/dashboards/logOnlyLogs.json @@ -0,0 +1,382 @@ +{ + "__inputs": [ + { + "name": "DS_LOKI", + "label": "Loki", + "description": "", + "type": "datasource", + "pluginId": "loki", + "pluginName": "Loki" + }, + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__elements": {}, + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "10.2.0" + }, + { + "type": "datasource", + "id": "loki", + "name": "Loki", + "version": "1.0.0" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Log entries of threats logged in log-only mode by Airlock Microgateway.\n\nThe dashboard can be filtered by namespace. Column filters on the table allow for an even more granular filtering of the logs.", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": null, + "links": [ + { + "asDropdown": true, + "icon": "external link", + "includeVars": true, + "keepTime": true, + "tags": [ + "airlock-microgateway" + ], + "targetBlank": true, + "title": "Airlock Microgateway", + "tooltip": "", + "type": "dashboards", + "url": "" + } + ], + "panels": [ + { + "datasource": { + "default": false, + "type": "loki", + "uid": "${DS_LOKI}" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "text", + "mode": "fixed" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "filterable": true, + "inspect": true + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Namespace" + }, + "properties": [ + { + "id": "custom.width", + "value": 328 + }, + { + "id": "custom.filterable" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Timestamp" + }, + "properties": [ + { + "id": "custom.width", + "value": 176 + }, + { + "id": "unit", + "value": "time: YYYY-MM-DD HH:mm:ss.SSS" + }, + { + "id": "custom.filterable" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "HTTP Method" + }, + "properties": [ + { + "id": "custom.width", + "value": 132 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Client IP" + }, + "properties": [ + { + "id": "custom.width", + "value": 137 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Request ID" + }, + "properties": [ + { + "id": "custom.width", + "value": 328 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Request Size" + }, + "properties": [ + { + "id": "custom.width", + "value": 126 + }, + { + "id": "unit", + "value": "bytes" + }, + { + "id": "custom.align", + "value": "right" + } + ] + } + ] + }, + "gridPos": { + "h": 27, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "enablePagination": true, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "10.2.0", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "editorMode": "code", + "expr": "{container=\"airlock-microgateway-engine\", namespace=~\"${namespace:regex}\"} |= `log_only` |= `envoy.access` | json http_method=\"http.request.method\", url=\"url.path\", domain=\"url.domain\", request_size=\"http.request.bytes\", client_ip=\"network.forwarded_ip\", request_id=\"http.request.id\", details=\"airlock.actions.log_only\", log_type=\"event.dataset\" | label_format log_count=`{{ len (fromJson .details) }}` | log_type = `envoy.access` | log_count > 0", + "hide": false, + "queryType": "range", + "refId": "Log Only Logs" + } + ], + "title": "Threats Logs Log-Only", + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "source": "labels" + } + }, + { + "id": "filterFieldsByName", + "options": { + "byVariable": false, + "include": { + "names": [ + "Time", + "client_ip", + "details", + "domain", + "http_method", + "namespace", + "request_id", + "request_size", + "url" + ] + } + } + }, + { + "id": "organize", + "options": { + "excludeByName": { + "Line": true, + "id": true, + "labelTypes": true, + "labels": true, + "tsNs": false + }, + "includeByName": {}, + "indexByName": { + "Time": 0, + "client_ip": 8, + "details": 7, + "domain": 2, + "http_method": 4, + "namespace": 1, + "request_id": 9, + "request_size": 6, + "url": 5 + }, + "renameByName": { + "Time": "Timestamp", + "client_ip": "Client IP", + "details": "Details", + "domain": "URL Domain", + "http_method": "HTTP Method", + "namespace": "Namespace", + "request_id": "Request ID", + "request_size": "Request Size", + "url": "URL Path" + } + } + } + ], + "type": "table" + } + ], + "schemaVersion": 39, + "tags": [ + "airlock-microgateway" + ], + "templating": { + "list": [ + { + "current": {}, + "hide": 2, + "includeAll": false, + "label": "DS_LOKI", + "multi": false, + "name": "DS_LOKI", + "options": [], + "query": "loki", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(microgateway_license_http_rq_total,namespace)", + "hide": 0, + "includeAll": true, + "label": "Application Namespace", + "multi": true, + "name": "namespace", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(microgateway_license_http_rq_total,namespace)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 5, + "type": "query" + }, + { + "current": {}, + "hide": 2, + "includeAll": false, + "label": "DS_PROMETHEUS", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + } + ] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Airlock Microgateway Threats LogOnly - Logs", + "uid": "adnasdfdwnyadcc", + "version": 7, + "weekStart": "" +} \ No newline at end of file diff --git a/charts/airlock/microgateway/4.4.0/dashboards/logOnlyMetrics.json b/charts/airlock/microgateway/4.4.0/dashboards/logOnlyMetrics.json new file mode 100644 index 000000000..137e28ee0 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/dashboards/logOnlyMetrics.json @@ -0,0 +1,621 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__elements": {}, + "__requires": [ + { + "type": "panel", + "id": "barchart", + "name": "Bar chart", + "version": "" + }, + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "10.2.0" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "timeseries", + "name": "Time series", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Metrics on threats logged by Airlock Microgateway in threat handling mode LogOnly.\n\nDashboard can be filtered by namespaces as well as block types.", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": null, + "links": [ + { + "asDropdown": true, + "icon": "external link", + "includeVars": true, + "keepTime": true, + "tags": [ + "airlock-microgateway" + ], + "targetBlank": true, + "title": "Airlock Microgateway", + "tooltip": "", + "type": "dashboards", + "url": "" + } + ], + "panels": [ + { + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 6, + "title": "Airlock Microgateway Threats LogOnly - Metrics", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Number of threats logged by Airlock Microgateway in threat handling mode LogOnly.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "text", + "mode": "fixed" + }, + "mappings": [ + { + "options": { + "match": "nan", + "result": { + "index": 0, + "text": "n/a" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 0, + "y": 1 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.2.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "disableTextWrap": false, + "editorMode": "code", + "exemplar": false, + "expr": "round(sum(increase(microgateway_http_downstream_rq_threats_logged_total{block_type=~\"${blockType:regex}\", namespace=~\"${namespace:regex}\", job=~\"${operator_namespace.regex}/.*-engine\"}[$__range])))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": true, + "legendFormat": "Logged threats in LogOnly mode", + "range": false, + "refId": "A", + "useBackend": false + } + ], + "title": "Threats - LogOnly", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Number of threats per second handled in LogOnly mode.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "orange", + "mode": "fixed" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "left", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 25, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 20, + "x": 0, + "y": 5 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "timezone": [ + "" + ], + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "sum(rate(microgateway_http_downstream_rq_threats_logged_total{block_type=~\"${blockType:regex}\", namespace=~\"${namespace:regex}\", job=~\"${operator_namespace.regex}/.*-engine\"}[1m]))", + "instant": false, + "legendFormat": "Number of threats per second", + "range": true, + "refId": "LogOnly Events" + } + ], + "title": "Threats - LogOnly", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Number of threats in LogOnly mode by block type.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "super-light-orange", + "mode": "fixed" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisGridShow": true, + "axisLabel": "", + "axisPlacement": "auto", + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 0, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 10, + "x": 0, + "y": 15 + }, + "id": 4, + "options": { + "barRadius": 0, + "barWidth": 0.8, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "orientation": "horizontal", + "showValue": "never", + "stacking": "none", + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "asc" + }, + "xField": "block_type", + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "pluginVersion": "10.2.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "round(sum by (block_type) (increase(microgateway_http_downstream_rq_threats_logged_total{block_type=~\"${blockType:regex}\", namespace=~\"${namespace:regex}\", job=~\"${operator_namespace.regex}/.*-engine\"}[$__range])))", + "format": "time_series", + "instant": true, + "legendFormat": "__auto", + "range": false, + "refId": "A" + } + ], + "title": "Block Type", + "transformations": [ + { + "id": "reduce", + "options": { + "includeTimeField": false, + "labelsToFields": true, + "mode": "seriesToRows", + "reducers": [ + "sum" + ] + } + } + ], + "type": "barchart" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Number of threats in LogOnly mode by block subtype, which are subsets of the various block types.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "light-orange", + "mode": "fixed" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 10, + "x": 10, + "y": 15 + }, + "id": 5, + "options": { + "barRadius": 0, + "barWidth": 0.8, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "orientation": "horizontal", + "showValue": "never", + "stacking": "none", + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + }, + "xField": "block_subtype", + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "pluginVersion": "10.2.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "round(sum by (block_subtype) (increase(microgateway_http_downstream_rq_threats_logged_total{block_type=~\"${blockType:regex}\", namespace=~\"${namespace:regex}\", job=~\"${operator_namespace.regex}/.*-engine\"}[$__range])))", + "instant": true, + "legendFormat": "__auto", + "range": false, + "refId": "A" + } + ], + "title": "Block Subtype", + "transformations": [ + { + "id": "reduce", + "options": { + "labelsToFields": true, + "reducers": [ + "sum" + ] + } + } + ], + "type": "barchart" + } + ], + "refresh": "", + "schemaVersion": 39, + "tags": [ + "airlock-microgateway" + ], + "templating": { + "list": [ + { + "current": {}, + "hide": 2, + "includeAll": false, + "label": "Datasource Prometheus", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": {}, + "hide": 2, + "includeAll": false, + "label": "DS_LOKI", + "multi": false, + "name": "DS_LOKI", + "options": [], + "query": "loki", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(microgateway_license_valid,namespace)", + "hide": 0, + "includeAll": true, + "label": "Operator Namespace", + "multi": true, + "name": "operator_namespace", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(microgateway_license_valid,namespace)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 2, + "regex": ".*", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(microgateway_license_http_rq_total,namespace)", + "hide": 0, + "includeAll": true, + "label": "Application Namespace", + "multi": true, + "name": "namespace", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(microgateway_license_http_rq_total,namespace)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 5, + "type": "query" + }, + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(microgateway_http_downstream_rq_threats_logged_total,block_type)", + "hide": 0, + "includeAll": true, + "label": "Block Type", + "multi": true, + "name": "blockType", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(microgateway_http_downstream_rq_threats_logged_total,block_type)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 5, + "type": "query" + } + ] + }, + "time": { + "from": "now-24h", + "to": "now" + }, + "timepicker": { + "hidden": false + }, + "timezone": "browser", + "title": "Airlock Microgateway Threats LogOnly - Metrics", + "uid": "ddnqoczu7qv2mfmsd3dd", + "version": 1, + "weekStart": "" +} \ No newline at end of file diff --git a/charts/airlock/microgateway/4.4.0/dashboards/overview.json b/charts/airlock/microgateway/4.4.0/dashboards/overview.json new file mode 100644 index 000000000..8a9c913b0 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/dashboards/overview.json @@ -0,0 +1,1134 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__elements": {}, + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "10.2.0" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + }, + { + "type": "panel", + "id": "timeseries", + "name": "Time series", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": null, + "links": [ + { + "asDropdown": true, + "icon": "external link", + "includeVars": true, + "keepTime": true, + "tags": [ + "airlock-microgateway" + ], + "targetBlank": true, + "title": "Airlock Microgateway", + "tooltip": "", + "type": "dashboards", + "url": "" + } + ], + "panels": [ + { + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 3, + "title": "Overview", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Number of pods that are protected by Airlock Microgateway.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 0, + "y": 1 + }, + "id": 11, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.2.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "sum(microgateway_sidecars{namespace=~\"${operator_namespace.regex}\"})", + "instant": true, + "legendFormat": "Protected Pods", + "range": false, + "refId": "A" + } + ], + "title": "Protected Pods", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Total number of requests processed by Airlock Microgateway.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "text", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 3, + "y": 1 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.2.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "disableTextWrap": false, + "editorMode": "code", + "exemplar": false, + "expr": "round(sum(increase(microgateway_license_http_rq_total{namespace=~\"${namespace:regex}\", job=~\"${operator_namespace.regex}/.*-engine\"}[$__range])))", + "format": "time_series", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": true, + "legendFormat": "Processed Requests", + "range": false, + "refId": "A", + "useBackend": false + } + ], + "title": "Requests", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Ratio of blocked requests vs. processed requests by Airlock Microgateway.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "text", + "mode": "fixed" + }, + "mappings": [ + { + "options": { + "match": "nan", + "result": { + "index": 0, + "text": "n/a" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 6, + "y": 1 + }, + "id": 5, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.2.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "disableTextWrap": false, + "editorMode": "code", + "exemplar": false, + "expr": "sum(increase(microgateway_http_downstream_rq_threats_blocked_total{namespace=~\"${namespace:regex}\", job=~\"${operator_namespace.regex}/.*-engine\"}[$__range])) / sum(increase(microgateway_license_http_rq_total{namespace=~\"${namespace:regex}\", job=~\"${operator_namespace.regex}/.*-engine\"}[$__range]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": true, + "legendFormat": "Blocked Requests (%)", + "range": false, + "refId": "A", + "useBackend": false + } + ], + "title": "% Blocked Requests", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "License status of Airlock Microgateway.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "color": "red", + "index": 1, + "text": "Invalid" + }, + "1": { + "color": "green", + "index": 0, + "text": "Valid" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 9, + "y": 1 + }, + "id": 10, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.2.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "min(microgateway_license_valid{namespace=~\"${operator_namespace.regex}\"})", + "instant": true, + "legendFormat": "License Status", + "range": false, + "refId": "Licenses" + } + ], + "title": "License", + "type": "stat" + }, + { + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 5 + }, + "id": 2, + "title": "Blocks", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Requests per second processed by Airlock Microgateway along with the corresponding block rate.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "blue", + "mode": "fixed" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "left", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "% Blocks" + }, + "properties": [ + { + "id": "custom.axisPlacement", + "value": "right" + }, + { + "id": "unit", + "value": "percentunit" + }, + { + "id": "color", + "value": { + "fixedColor": "orange", + "mode": "fixed" + } + }, + { + "id": "max", + "value": 1 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Requests per second" + }, + "properties": [ + { + "id": "unit", + "value": "short" + }, + { + "id": "custom.fillOpacity", + "value": 25 + } + ] + } + ] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 0, + "y": 6 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "timezone": [ + "" + ], + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "sum(rate(microgateway_license_http_rq_total{namespace=~\"${namespace:regex}\", job=~\"${operator_namespace.regex}/.*-engine\"}[1m]))", + "instant": false, + "legendFormat": "Requests per second", + "range": true, + "refId": "Requests per Second" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(rate(microgateway_http_downstream_rq_threats_blocked_total{namespace=~\"${namespace:regex}\", job=~\"${operator_namespace.regex}/.*-engine\"}[1m])) / sum(rate(microgateway_license_http_rq_total{namespace=~\"${namespace:regex}\", job=~\"${operator_namespace.regex}/.*-engine\"}[1m]))", + "hide": false, + "instant": false, + "legendFormat": "% Blocks", + "range": true, + "refId": "Blocks" + } + ], + "title": "Requests vs. % Blocks", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Threats blocked by Airlock Microgateway categorized by their corresponding block type.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "barAlignment": 0, + "drawStyle": "line", + "gradientMode": "none", + "hideValue": false, + "lineInterpolation": "linear", + "lineStyle": { + "dash": [ + 10, + 10 + ], + "fill": "solid" + }, + "showPoints": "never", + "spanNulls": false, + "type": "sparkline" + }, + "inspect": false + }, + "displayName": "Block Type", + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "block_type" + }, + "properties": [ + { + "id": "custom.width", + "value": 153 + }, + { + "id": "custom.cellOptions", + "value": { + "type": "auto" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Trend #Block Types" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "orange", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 12, + "y": 6 + }, + "id": 7, + "options": { + "cellHeight": "lg", + "footer": { + "countRows": false, + "enablePagination": false, + "fields": [ + "Value" + ], + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": false, + "sortBy": [ + { + "desc": true, + "displayName": "block_type" + } + ] + }, + "pluginVersion": "10.2.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum by (block_type) (increase(microgateway_http_downstream_rq_threats_blocked_total{namespace=~\"${namespace:regex}\", job=~\"${operator_namespace.regex}/.*-engine\"}[1m] offset -1m))/(60000/$__interval_ms)", + "format": "time_series", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "Block Types" + } + ], + "title": "Blocked Threats by Block Type", + "transformations": [ + { + "id": "timeSeriesTable", + "options": { + "A": { + "timeField": "Time" + }, + "Block Types": { + "stat": "sum", + "timeField": "Time" + } + } + } + ], + "type": "table" + }, + { + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 16 + }, + "id": 1, + "title": "Latency", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Percentiles of the application downstream latency over one minute.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "ms" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "25th Percentile" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "super-light-purple", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "50th Percentile" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "purple", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "95th Percentile" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "dark-purple", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 17 + }, + "id": 8, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.25, sum(rate(envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix=\"http\", namespace=~\"${namespace:regex}\", job=~\"${operator_namespace.regex}/.*-engine\"}[1m])) by (le))", + "instant": false, + "legendFormat": "25th Percentile", + "range": true, + "refId": "25th Percentile" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.5, sum(rate(envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix=\"http\", namespace=~\"${namespace:regex}\", job=~\"${operator_namespace.regex}/.*-engine\"}[1m])) by (le))", + "hide": false, + "instant": false, + "legendFormat": "50th Percentile", + "range": true, + "refId": "50th Percentile" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.95, sum(rate(envoy_http_downstream_rq_time_bucket{envoy_http_conn_manager_prefix=\"http\", namespace=~\"${namespace:regex}\", job=~\"${operator_namespace.regex}/.*-engine\"}[1m])) by (le))", + "hide": false, + "instant": false, + "legendFormat": "95th Percentile", + "range": true, + "refId": "95th Percentile" + } + ], + "title": "Application Downstream Latency", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Percentiles of the Airlock Microgateway processing time over one minute.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "ms" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "25th Percentile" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "super-light-purple", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "50th Percentile" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "purple", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "95th Percentile" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "dark-purple", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 17 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.25, sum(rate(microgateway_rq_processing_time_ms_bucket{namespace=~\"${namespace:regex}\", job=~\"${operator_namespace.regex}/.*-engine\"}[1m])) by (le))", + "instant": false, + "legendFormat": "25th Percentile", + "range": true, + "refId": "0.25 Percentile" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.5, sum(rate(microgateway_rq_processing_time_ms_bucket{namespace=~\"${namespace:regex}\", job=~\"${operator_namespace.regex}/.*-engine\"}[1m])) by (le))", + "hide": false, + "instant": false, + "legendFormat": "50th Percentile", + "range": true, + "refId": "0.5 Percentile" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.95, sum(rate(microgateway_rq_processing_time_ms_bucket{namespace=~\"${namespace:regex}\", job=~\"${operator_namespace.regex}/.*-engine\"}[1m])) by (le))", + "hide": false, + "instant": false, + "legendFormat": "95th Percentile", + "range": true, + "refId": "0.95 Percentile" + } + ], + "title": "Airlock Microgateway Processing Time", + "type": "timeseries" + } + ], + "refresh": "", + "schemaVersion": 39, + "tags": [ + "airlock-microgateway" + ], + "templating": { + "list": [ + { + "current": {}, + "hide": 2, + "includeAll": false, + "label": "DS_PROMETHEUS", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(microgateway_license_valid,namespace)", + "hide": 0, + "includeAll": true, + "label": "Operator Namespace", + "multi": true, + "name": "operator_namespace", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(microgateway_license_valid,namespace)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 2, + "regex": ".*", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(microgateway_license_http_rq_total,namespace)", + "hide": 0, + "includeAll": true, + "label": "Application Namespace", + "multi": true, + "name": "namespace", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(microgateway_license_http_rq_total,namespace)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-24h", + "to": "now" + }, + "timeRangeUpdatedDuringEditOrView": false, + "timepicker": {}, + "timezone": "browser", + "title": "Airlock Microgateway Overview", + "uid": "fdp5jb8fnrmyoa", + "version": 1, + "weekStart": "" +} \ No newline at end of file diff --git a/charts/airlock/microgateway/4.4.0/templates/NOTES.txt b/charts/airlock/microgateway/4.4.0/templates/NOTES.txt new file mode 100644 index 000000000..a607483f9 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/templates/NOTES.txt @@ -0,0 +1,61 @@ +Thank you for installing Airlock Microgateway. +{{- if .Values.operator.gatewayAPI.enabled }} + +K8s Gateway API support enabled. +Note that the K8s Gateway API support is an incubating Airlock Microgateway feature. We encourage you to try the installation and configuration for testing and evaluation. Your feedback is welcome. + + {{- if or .Values.operator.watchNamespaces .Values.operator.watchNamespaceSelector -}} + {{- fail ` + +K8s Gateway API is only supported using the 'AllNamespaces' installation mode type, ensure that 'operator.watchNamespaces' and 'operator.watchNamespaceSelector' are not configured. +` + -}} + {{- end -}} +{{- end }} + +Please ensure the following prerequisites are fulfilled: +* cert-manager is installed. + https://cert-manager.io/docs/installation/helm/ +* A valid Airlock Microgateway license is deployed in the Kubernetes secret '{{ .Release.Namespace }}/{{ .Values.license.secretName }}' + * Get a free Community license: https://airlock.com/en/microgateway-community + * Order a Premium license: https://airlock.com/en/microgateway-premium +* Airlock Microgateway CNI is installed on the cluster, when running data plane mode sidecar + https://artifacthub.io/packages/helm/airlock-microgateway-cni/microgateway-cni. + For more information about data plane modes, see https://docs.airlock.com/microgateway/{{ include "airlock-microgateway.docsVersion" . }}/#data/1660804709650.html + +Further information: +* Documentation: https://docs.airlock.com/microgateway/{{ include "airlock-microgateway.docsVersion" . }} +* CRD API reference documentation: https://docs.airlock.com/microgateway/{{ include "airlock-microgateway.docsVersion" . }}/api/crds +* Airlock Microgateway Labs: https://play.instruqt.com/airlock/invite/hyi9fy4b4jzc?icp_referrer=helm +{{- if .Values.crds.skipVersionCheck }} + +Warning: CRD version check skipped +{{- else -}} +{{- $outdatedCRDs := (include "airlock-microgateway.outdatedCRDs" .) -}} +{{- if $outdatedCRDs -}} + {{- fail (printf ` + +Helm does not automatically upgrade CRDs from the chart's 'crds/' directory during 'helm install/upgrade'. +Therefore, the CRDs must be manually upgraded with the following command before deploying this chart: + +kubectl apply -k https://github.com/airlock/microgateway/deploy/charts/airlock-microgateway/crds/?ref=%s --server-side --force-conflicts + +If you are not using the helm install/upgrade command and instead rely on some other mechanism which is able to upgrade CRDs for deploying this chart, you can suppress this error by setting the helm value 'crds.skipVersionCheck=true'.` + .Chart.AppVersion) + -}} +{{- end -}} +{{- end -}} +{{- if .Values.tests.enabled -}} + {{- if .Values.operator.watchNamespaces -}} + {{- if not (has .Release.Namespace .Values.operator.watchNamespaces) -}} + {{- fail (printf ` + +To execute 'helm test', it is necessary that the release namespace '%s' is part of the operator's watch scope. Either disable the tests or ensure that the release namespace is added to watch namspace list ('operator.watchNamespaces') in the helm values. +` + .Release.Namespace) + -}} + {{- end -}} + {{- end -}} +{{- end }} + +Your release version is {{ .Chart.Version }}. \ No newline at end of file diff --git a/charts/airlock/microgateway/4.4.0/templates/_helpers.tpl b/charts/airlock/microgateway/4.4.0/templates/_helpers.tpl new file mode 100644 index 000000000..733ba9648 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/templates/_helpers.tpl @@ -0,0 +1,153 @@ +{{/* +Expand the name of the chart. +We truncate at 49 chars because some Kubernetes name fields are limited to 63 chars (by the DNS naming spec) +and the longest explicit suffix is 14 characters. +*/}} +{{- define "airlock-microgateway.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 49 | trimSuffix "-" }} +{{- end }} + +{{/* +Convert an image configuration object into an image ref string. +*/}} +{{- define "airlock-microgateway.image" -}} + {{- if .digest -}} + {{- printf "%s@%s" .repository .digest -}} + {{- else if .tag -}} + {{- printf "%s:%s" .repository .tag -}} + {{- else -}} + {{- printf "%s" .repository -}} + {{- end -}} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 36 chars because some Kubernetes name fields are limited to 63 chars (by the DNS naming spec) +and the longest implicit suffix is 27 characters. +If release name contains chart name it will be used as a full name. +*/}} +{{- define "airlock-microgateway.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 36 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 36 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 36 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "airlock-microgateway.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "airlock-microgateway.sharedLabels" -}} +helm.sh/chart: {{ include "airlock-microgateway.chart" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/part-of: {{ .Chart.Name }} +{{- with .Values.commonLabels }} +{{ toYaml .}} +{{- end }} +{{- end }} + +{{/* +Common Selector labels +*/}} +{{- define "airlock-microgateway.sharedSelectorLabels" -}} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Restricted Container Security Context +*/}} +{{- define "airlock-microgateway.restrictedSecurityContext" -}} +allowPrivilegeEscalation: false +privileged: false +runAsNonRoot: true +capabilities: + drop: ["ALL"] +readOnlyRootFilesystem: true +seccompProfile: + type: RuntimeDefault +{{- end }} + +{{/* Precondition: May only be used if AppVersion is isSemver */}} +{{- define "airlock-microgateway.supportedCRDVersionPattern" -}} +{{- $version := (semver .Chart.AppVersion) -}} +{{- if $version.Prerelease -}} +>= {{ $version.Major }}.{{ $version.Minor }}.{{ $version.Patch }}-{{ $version.Prerelease }} +{{- else -}} +>= {{ $version.Major }}.{{ $version.Minor }}.0 || >= {{ $version.Major }}.{{ $version.Minor }}.{{ add1 $version.Patch }}-0 +{{- end -}} +{{- end -}} + +{{- define "airlock-microgateway.outdatedCRDs" -}} +{{- if (eq "true" (include "airlock-microgateway.isSemver" .Chart.AppVersion)) -}} + {{- $supportedVersion := (include "airlock-microgateway.supportedCRDVersionPattern" .) -}} + {{- range $path, $_ := .Files.Glob "crds/*.yaml" -}} + {{- $api := ($.Files.Get $path | fromYaml).metadata.name -}} + {{- $crd := (lookup "apiextensions.k8s.io/v1" "CustomResourceDefinition" "" $api) -}} + {{- $isOutdated := false -}} + {{- if $crd -}} + {{/* If CRD is already present in the cluster, it must have the minimum supported version */}} + {{- $isOutdated = true -}} + {{- if hasKey $crd.metadata "labels" -}} + {{- $crdVersion := get $crd.metadata.labels "app.kubernetes.io/version" -}} + {{- if (eq "true" (include "airlock-microgateway.isSemver" $crdVersion)) -}} + {{- if (semverCompare $supportedVersion $crdVersion) }} + {{- $isOutdated = false -}} + {{- end }} + {{- end -}} + {{- end -}} + {{- end -}} + {{- if $isOutdated }} +{{ base $path }} + {{- end }} + {{- end -}} +{{- end -}} +{{- end -}} + +{{- define "airlock-microgateway.isSemver" -}} +{{- regexMatch `^(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-]+)*))?$` . -}} +{{- end -}} + +{{- define "airlock-microgateway.docsVersion" -}} +{{- if and (eq "true" (include "airlock-microgateway.isSemver" .Chart.AppVersion)) (not (contains "-" .Chart.AppVersion)) -}} + {{- $version := (semver .Chart.AppVersion) -}} + {{- $version.Major }}.{{ $version.Minor -}} +{{- else -}} + {{- print "latest" -}} +{{- end -}} +{{- end -}} + +{{- define "airlock-microgateway.watchNamespaceSelector.labelQuery" -}} +{{- $list := list -}} +{{- with .matchLabels -}} + {{- range $key, $value := . -}} + {{- $list = append $list (printf "%s=%s" $key $value) -}} + {{- end -}} +{{- end -}} +{{- with .matchExpressions -}} + {{- range . -}} + {{- if has .operator (list "In" "NotIn") -}} + {{- $list = append $list (printf "%s %s (%s)" .key (lower .operator) (join "," .values)) -}} + {{- else if eq .operator "Exists" -}} + {{- $list = append $list .key -}} + {{- else if eq .operator "DoesNotExist" -}} + {{- $list = append $list (printf "!%s" .key) -}} + {{- end -}} + {{- end -}} +{{- end -}} +{{- join "," $list -}} +{{- end -}} \ No newline at end of file diff --git a/charts/airlock/microgateway/4.4.0/templates/operator/_operator_helpers.tpl b/charts/airlock/microgateway/4.4.0/templates/operator/_operator_helpers.tpl new file mode 100644 index 000000000..a540ff9f4 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/templates/operator/_operator_helpers.tpl @@ -0,0 +1,42 @@ +{{/* +Create a default fully qualified name for operator components. +*/}} +{{- define "airlock-microgateway.operator.fullname" -}} +{{ include "airlock-microgateway.fullname" . }}-operator +{{- end }} + + +{{/* +Common operator labels +*/}} +{{- define "airlock-microgateway.operator.labels" -}} +{{ include "airlock-microgateway.sharedLabels" . }} +{{ include "airlock-microgateway.operator.selectorLabels" . }} +{{- end }} + +{{/* +Operator Selector labels +*/}} +{{- define "airlock-microgateway.operator.selectorLabels" -}} +{{ include "airlock-microgateway.sharedSelectorLabels" . }} +app.kubernetes.io/name: {{ include "airlock-microgateway.name" . }}-operator +app.kubernetes.io/component: controller +{{- end }} + +{{/* +Create the name of the service account to use for the operator +*/}} +{{- define "airlock-microgateway.operator.serviceAccountName" -}} +{{- if .Values.operator.serviceAccount.create }} +{{- default (include "airlock-microgateway.operator.fullname" .) .Values.operator.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.operator.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +ServiceMonitor metrics regex pattern for leader only metrics +*/}} +{{- define "airlock-microgateway.operator.metricsLeaderOnlyRegexPattern" -}} +^(microgateway_license|microgateway_sidecars).*$ +{{- end }} diff --git a/charts/airlock/microgateway/4.4.0/templates/operator/_rbac.gen.tpl b/charts/airlock/microgateway/4.4.0/templates/operator/_rbac.gen.tpl new file mode 100644 index 000000000..faa078b6b --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/templates/operator/_rbac.gen.tpl @@ -0,0 +1,206 @@ +{{/* AUTOGENERATED FILE DO NOT EDIT */}} + +{{/* +Operator rbac permission rules +*/}} +{{- define "airlock-microgateway-operator.rbacRules" -}} +- apiGroups: + - "" + resources: + - configmaps + - namespaces + - replicasets + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - "" + resources: + - pods + verbs: + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - pods/finalizers + verbs: + - update +- apiGroups: + - "" + resources: + - pods/status + verbs: + - patch + - update +- apiGroups: + - "" + resources: + - secrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - services + verbs: + - create + - get + - list + - patch + - update + - watch +- apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - get + - list + - watch +- apiGroups: + - apps + resources: + - deployments + verbs: + - create + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - replicasets + verbs: + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - replicasets/finalizers + verbs: + - patch + - update +- apiGroups: + - gateway.networking.k8s.io + resources: + - gatewayclasses + verbs: + - get + - list + - patch + - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - gatewayclasses/finalizers + - gatewayclasses/status + - gateways/finalizers + - gateways/status + - httproutes/status + verbs: + - patch + - update +- apiGroups: + - gateway.networking.k8s.io + resources: + - gateways + - httproutes + - referencegrants + verbs: + - get + - list + - watch +- apiGroups: + - microgateway.airlock.com + resources: + - accesscontrols + - contentsecurities + - contentsecuritypolicies + - denyrules + - envoyclusters + - envoyhttpfilters + - graphqls + - headerrewrites + - identitypropagations + - jwks + - limits + - oidcproviders + - oidcrelyingparties + - openapis + - parsers + - redisproviders + - sessionhandlings + - telemetries + verbs: + - get + - list + - watch +- apiGroups: + - microgateway.airlock.com + resources: + - contentsecuritypolicies/status + verbs: + - patch + - update +- apiGroups: + - microgateway.airlock.com + resources: + - envoyconfigurations + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - microgateway.airlock.com + resources: + - envoyconfigurations/status + - sidecargateways/status + verbs: + - get + - patch + - update +- apiGroups: + - microgateway.airlock.com + resources: + - sidecargateways + verbs: + - get + - list + - patch + - update + - watch +- apiGroups: + - microgateway.airlock.com + resources: + - sidecargateways/finalizers + verbs: + - update +{{- end }} diff --git a/charts/airlock/microgateway/4.4.0/templates/operator/_webhooks.gen.tpl b/charts/airlock/microgateway/4.4.0/templates/operator/_webhooks.gen.tpl new file mode 100644 index 000000000..97474df39 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/templates/operator/_webhooks.gen.tpl @@ -0,0 +1,399 @@ +{{/* AUTOGENERATED FILE DO NOT EDIT */}} + +{{/* +Operator mutating webhooks +*/}} +{{- define "airlock-microgateway-operator.mutatingWebhooks" -}} +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: airlock-microgateway-operator-webhook + namespace: '{{ .Release.Namespace }}' + path: /mutate-v1-pod + failurePolicy: Fail + name: mutate-pod.microgateway.airlock.com + reinvocationPolicy: IfNeeded + rules: + - apiGroups: + - "" + apiVersions: + - v1 + operations: + - CREATE + resources: + - pods + sideEffects: None + objectSelector: + matchLabels: + sidecar.microgateway.airlock.com/inject: "true" +{{- end }} + +{{/* +Operator validating webhooks +*/}} +{{- define "airlock-microgateway-operator.validatingWebhooks" -}} +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: airlock-microgateway-operator-webhook + namespace: '{{ .Release.Namespace }}' + path: /validate-v1-pod + failurePolicy: Fail + name: validate-pod.microgateway.airlock.com + rules: + - apiGroups: + - "" + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - pods + sideEffects: None + objectSelector: + matchLabels: + sidecar.microgateway.airlock.com/inject: "true" +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: airlock-microgateway-operator-webhook + namespace: '{{ .Release.Namespace }}' + path: /validate-microgateway-airlock-com-v1alpha1-accesscontrol + failurePolicy: Fail + name: validate-accesscontrol.microgateway.airlock.com + rules: + - apiGroups: + - microgateway.airlock.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - accesscontrols + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: airlock-microgateway-operator-webhook + namespace: '{{ .Release.Namespace }}' + path: /validate-microgateway-airlock-com-v1alpha1-contentsecuritypolicy + failurePolicy: Fail + name: validate-contentsecuritypolicy.microgateway.airlock.com + rules: + - apiGroups: + - microgateway.airlock.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - contentsecuritypolicies + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: airlock-microgateway-operator-webhook + namespace: '{{ .Release.Namespace }}' + path: /validate-microgateway-airlock-com-v1alpha1-denyrules + failurePolicy: Fail + name: validate-denyrules.microgateway.airlock.com + rules: + - apiGroups: + - microgateway.airlock.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - denyrules + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: airlock-microgateway-operator-webhook + namespace: '{{ .Release.Namespace }}' + path: /validate-microgateway-airlock-com-v1alpha1-envoycluster + failurePolicy: Fail + name: validate-envoycluster.microgateway.airlock.com + rules: + - apiGroups: + - microgateway.airlock.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - envoyclusters + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: airlock-microgateway-operator-webhook + namespace: '{{ .Release.Namespace }}' + path: /validate-microgateway-airlock-com-v1alpha1-envoyhttpfilter + failurePolicy: Fail + name: validate-envoyhttpfilter.microgateway.airlock.com + rules: + - apiGroups: + - microgateway.airlock.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - envoyhttpfilters + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: airlock-microgateway-operator-webhook + namespace: '{{ .Release.Namespace }}' + path: /validate-microgateway-airlock-com-v1alpha1-graphql + failurePolicy: Fail + name: validate-graphql.microgateway.airlock.com + rules: + - apiGroups: + - microgateway.airlock.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - graphqls + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: airlock-microgateway-operator-webhook + namespace: '{{ .Release.Namespace }}' + path: /validate-microgateway-airlock-com-v1alpha1-headerrewrites + failurePolicy: Fail + name: validate-headerrewrites.microgateway.airlock.com + rules: + - apiGroups: + - microgateway.airlock.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - headerrewrites + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: airlock-microgateway-operator-webhook + namespace: '{{ .Release.Namespace }}' + path: /validate-microgateway-airlock-com-v1alpha1-identitypropagation + failurePolicy: Fail + name: validate-identitypropagation.microgateway.airlock.com + rules: + - apiGroups: + - microgateway.airlock.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - identitypropagations + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: airlock-microgateway-operator-webhook + namespace: '{{ .Release.Namespace }}' + path: /validate-microgateway-airlock-com-v1alpha1-jwks + failurePolicy: Fail + name: validate-jwks.microgateway.airlock.com + rules: + - apiGroups: + - microgateway.airlock.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - jwks + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: airlock-microgateway-operator-webhook + namespace: '{{ .Release.Namespace }}' + path: /validate-microgateway-airlock-com-v1alpha1-limits + failurePolicy: Fail + name: validate-limits.microgateway.airlock.com + rules: + - apiGroups: + - microgateway.airlock.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - limits + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: airlock-microgateway-operator-webhook + namespace: '{{ .Release.Namespace }}' + path: /validate-microgateway-airlock-com-v1alpha1-oidcprovider + failurePolicy: Fail + name: validate-oidcprovider.microgateway.airlock.com + rules: + - apiGroups: + - microgateway.airlock.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - oidcproviders + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: airlock-microgateway-operator-webhook + namespace: '{{ .Release.Namespace }}' + path: /validate-microgateway-airlock-com-v1alpha1-oidcrelyingparty + failurePolicy: Fail + name: validate-oidcrelyingparty.microgateway.airlock.com + rules: + - apiGroups: + - microgateway.airlock.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - oidcrelyingparties + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: airlock-microgateway-operator-webhook + namespace: '{{ .Release.Namespace }}' + path: /validate-microgateway-airlock-com-v1alpha1-openapi + failurePolicy: Fail + name: validate-openapi.microgateway.airlock.com + rules: + - apiGroups: + - microgateway.airlock.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - openapis + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: airlock-microgateway-operator-webhook + namespace: '{{ .Release.Namespace }}' + path: /validate-microgateway-airlock-com-v1alpha1-parser + failurePolicy: Fail + name: validate-parser.microgateway.airlock.com + rules: + - apiGroups: + - microgateway.airlock.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - parsers + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: airlock-microgateway-operator-webhook + namespace: '{{ .Release.Namespace }}' + path: /validate-microgateway-airlock-com-v1alpha1-redisprovider + failurePolicy: Fail + name: validate-redisprovider.microgateway.airlock.com + rules: + - apiGroups: + - microgateway.airlock.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - redisproviders + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: airlock-microgateway-operator-webhook + namespace: '{{ .Release.Namespace }}' + path: /validate-microgateway-airlock-com-v1alpha1-sessionhandling + failurePolicy: Fail + name: validate-sessionhandling.microgateway.airlock.com + rules: + - apiGroups: + - microgateway.airlock.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - sessionhandlings + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: airlock-microgateway-operator-webhook + namespace: '{{ .Release.Namespace }}' + path: /validate-microgateway-airlock-com-v1alpha1-sidecargateway + failurePolicy: Fail + name: validate-sidecargateway.microgateway.airlock.com + rules: + - apiGroups: + - microgateway.airlock.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - sidecargateways + sideEffects: None +{{- end }} diff --git a/charts/airlock/microgateway/4.4.0/templates/operator/configmap.yaml b/charts/airlock/microgateway/4.4.0/templates/operator/configmap.yaml new file mode 100644 index 000000000..276a632e8 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/templates/operator/configmap.yaml @@ -0,0 +1,405 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "airlock-microgateway.operator.fullname" . }}-config + namespace: {{ .Release.Namespace }} + labels: + {{- include "airlock-microgateway.operator.labels" . | nindent 4 }} + {{- with .Values.commonAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +data: + engine_bootstrap_config_template.yaml: | + # Base configuration, admin interface on port 19000 + admin: + address: + socket_address: + address: 127.0.0.1 + port_value: 19000 + dynamic_resources: + cds_config: + initial_fetch_timeout: 10s + resource_api_version: V3 + api_config_source: + api_type: GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + # Prevent Envoy Node from overloading the xDS server due to rejected configuration when using xDS SotW gRPC + rate_limit_settings: + max_tokens: 5 + fill_rate: 0.2 + lds_config: + resource_api_version: V3 + initial_fetch_timeout: 10s + api_config_source: + api_type: GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + # Prevent Envoy Node from overloading the xDS server due to rejected configuration when using xDS SotW gRPC + rate_limit_settings: + max_tokens: 5 + fill_rate: 0.2 + static_resources: + listeners: + - name: probe + address: + socket_address: + address: 0.0.0.0 + port_value: 19001 + filter_chains: + - filters: + - name: http_connection_manager + typed_config: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: probe + codec_type: AUTO + http2_protocol_options: + initial_connection_window_size: 1048576 + initial_stream_window_size: 65536 + max_concurrent_streams: 100 + route_config: + name: probe + virtual_hosts: + - name: probe + domains: + - '*' + routes: + - name: ready + match: + path: /ready + headers: + - name: ':method' + string_match: + exact: 'GET' + route: + cluster: airlock_microgateway_engine_admin + http_filters: + - name: envoy.filters.http.router + typed_config: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + - name: metrics + address: + socket_address: + address: 0.0.0.0 + port_value: 19002 + filter_chains: + - filters: + - name: http_connection_manager + typed_config: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: metrics + codec_type: AUTO + http2_protocol_options: + initial_connection_window_size: 1048576 + initial_stream_window_size: 65536 + max_concurrent_streams: 100 + route_config: + name: metrics + virtual_hosts: + - name: metrics + domains: + - '*' + routes: + - name: metrics + match: + path: /metrics + headers: + - name: ':method' + string_match: + exact: 'GET' + route: + prefix_rewrite: '/stats/prometheus' + cluster: airlock_microgateway_engine_admin + http_filters: + - name: envoy.filters.http.router + typed_config: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - name: xds_cluster + connect_timeout: 1s + type: STRICT_DNS + load_assignment: + cluster_name: xds_cluster + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: airlock-microgateway-operator-xds.$(OPERATOR_NAMESPACE).svc.cluster.local + port_value: 13377 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 360s + timeout: 5s + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_minimum_protocol_version: TLSv1_3 + tls_maximum_protocol_version: TLSv1_3 + validation_context_sds_secret_config: + name: validation_context_sds + sds_config: + resource_api_version: V3 + path_config_source: + path: /etc/envoy/validation_context_sds_secret.yaml + watched_directory: + path: /etc/envoy/ + tls_certificate_sds_secret_configs: + - name: tls_certificate_sds + sds_config: + resource_api_version: V3 + path_config_source: + path: /etc/envoy/tls_certificate_sds_secret.yaml + watched_directory: + path: /etc/envoy/ + - name: airlock_microgateway_engine_admin + connect_timeout: 1s + type: STATIC + load_assignment: + cluster_name: airlock_microgateway_engine_admin + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: 127.0.0.1 + port_value: 19000 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 360s + timeout: 5s + stats_config: + stats_tags: + - tag_name: "block_type" + regex: "\\.(block_type\\.([^.]+))" + - tag_name: "block_subtype" + regex: "\\.(block_subtype\\.([^.]+))" + - tag_name: "envoy_cluster_name" + regex: "\\.(cluster\\.([^.]+))" + - tag_name: "version" + regex: "\\.(version\\.([^.]+))" + use_all_default_tags: true + overload_manager: + resource_monitors: + - name: "envoy.resource_monitors.global_downstream_max_connections" + typed_config: + "@type": type.googleapis.com/envoy.extensions.resource_monitors.downstream_connections.v3.DownstreamConnectionsConfig + max_active_downstream_connections: 50000 + bootstrap_extensions: + - name: airlock.bootstrap.engine_build_info + typed_config: + '@type': type.googleapis.com/airlock.extensions.bootstrap.stats.v1alpha.Stats + application_log_config: + log_format: + text_format: '{"@timestamp":"%Y-%m-%dT%T.%e%z","log":{"logger":"%n","level":"%l","origin":{"file":{"name":"%g","line":%#},"function":"%!"}},"event":{"module":"envoy","dataset":"envoy.application"},"process":{"pid":%P,"thread":{"id":%t}},"ecs":{"version":"8.5"},"message":"%j"}' + engine_container_template.yaml: | + name: "$(ENGINE_NAME)" + image: "$(ENGINE_IMAGE)" + imagePullPolicy: {{ .Values.engine.image.pullPolicy }} + args: + - "--config-path" + - "/etc/envoy/bootstrap_config.yaml" + - "--base-id" + - "$(BASE_ID)" + - "--file-flush-interval-msec" + - '1000' + - "--drain-time-s" + - '60' + - "--service-node" + - "$(POD_NAME).$(POD_NAMESPACE)" + - "--service-cluster" + - "$(APP_NAME).$(POD_NAMESPACE)" + - "--log-path" + - "/dev/stdout" + - "--log-level" + - "$(LOG_LEVEL)" + volumeMounts: + - name: airlock-microgateway-bootstrap-secret-volume + mountPath: /etc/envoy + readOnly: true + env: + - name: POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: POD_IP + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: status.podIP + ports: + - containerPort: 13378 + protocol: TCP + - containerPort: 19001 + protocol: TCP + - containerPort: 19002 + protocol: TCP + livenessProbe: + httpGet: + path: /ready + port: 19001 + scheme: HTTP + initialDelaySeconds: 5 + periodSeconds: 5 + failureThreshold: 5 + successThreshold: 1 + timeoutSeconds: 1 + readinessProbe: + httpGet: + path: /ready + port: 19001 + scheme: HTTP + initialDelaySeconds: 5 + periodSeconds: 5 + failureThreshold: 3 + successThreshold: 1 + timeoutSeconds: 1 + securityContext: + {{- include "airlock-microgateway.restrictedSecurityContext" . | nindent 6 }} + runAsUser: $(SECURITYCONTEXT_UID) + {{- with .Values.engine.resources }} + resources: + {{- toYaml . | nindent 6 }} + {{- end }} + session_agent_container_template.yaml: | + name: "$(SESSION_AGENT_NAME)" + image: "$(SESSION_AGENT_IMAGE)" + imagePullPolicy: {{ .Values.sessionAgent.image.pullPolicy }} + args: + - "--port" + - "19004" + - "--config-path" + - "/etc/microgateway-session-agent/config.json" + volumeMounts: + - name: airlock-microgateway-session-agent-volume + mountPath: /etc/microgateway-session-agent + readOnly: true + env: + - name: POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + ports: + - containerPort: 19004 + livenessProbe: + {{- if (semverCompare ">=1.27 || >=1.27.1-0" .Capabilities.KubeVersion.Version)}} + grpc: + port: 19004 + {{- else }} + tcpSocket: + port: 19004 + {{- end }} + initialDelaySeconds: 5 + periodSeconds: 5 + failureThreshold: 5 + successThreshold: 1 + timeoutSeconds: 5 + readinessProbe: + {{- if (semverCompare ">=1.27 || >=1.27.1-0" .Capabilities.KubeVersion.Version)}} + grpc: + port: 19004 + {{- else }} + tcpSocket: + port: 19004 + {{- end }} + initialDelaySeconds: 5 + periodSeconds: 5 + failureThreshold: 3 + successThreshold: 1 + timeoutSeconds: 5 + securityContext: + {{- include "airlock-microgateway.restrictedSecurityContext" . | nindent 6 }} + runAsUser: $(SECURITYCONTEXT_UID) + {{- with .Values.sessionAgent.resources }} + resources: + {{- toYaml . | nindent 6 }} + {{- end }} + network_validator_container_template.yaml: | + name: "$(NETWORK_VALIDATOR_NAME)" + image: "$(NETWORK_VALIDATOR_IMAGE)" + imagePullPolicy: {{ .Values.networkValidator.image.pullPolicy }} + command: ["/bin/sh", "-c"] + args: + - |- + echo 'pong' | nc -v -l 127.0.0.1 13378 & + for i in 1 2 3; do + sleep 1s + if r=$(echo 'ping' | nc -v -q 0 127.0.0.1 19003) && [ $r == pong ]; then + echo -n 'Traffic redirection to Airlock Microgateway Engine is working.' > /dev/termination-log + exit 0 + fi + done + echo -en 'Traffic redirection to Airlock Microgateway Engine is not working.\nRestart the pod after ensuring that hostNetwork is disabled and a compatible Airlock Microgateway CNI version is installed on the node.\nCertain environments may also require additional configuration (see docs.airlock.com for more information).' > /dev/termination-log + exit 1 + securityContext: + {{- include "airlock-microgateway.restrictedSecurityContext" . | nindent 6 }} + runAsUser: $(SECURITYCONTEXT_UID) + {{- with .Values.networkValidator.resources }} + resources: + {{- toYaml . | nindent 6 }} + {{- end }} + operator_config.yaml: | + apiVersion: config.airlock.com/v1alpha1 + kind: OperatorConfig + health: + healthProbeBindAddress: :8081 + metrics: + bindAddress: 0.0.0.0:8080 + webhook: + port: 9443 + deployment: + sidecar: + engineContainerTemplate: "/sidecar/engine_container_template.yaml" + networkValidatorContainerTemplate: "/sidecar/network_validator_container_template.yaml" + sessionAgentContainerTemplate: "/sidecar/session_agent_container_template.yaml" + engine: + bootstrapConfigTemplate: "/engine_bootstrap_config_template.yaml" + log: + level: {{ .Values.operator.config.logLevel }} + {{- with $.Values.operator.watchNamespaceSelector }} + namespaces: + selector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $.Values.operator.watchNamespaces }} + namespaces: + list: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $.Values.operator.gatewayAPI }} + gatewayAPI: + enabled: {{ .enabled }} + {{- if .controllerName }} + controllerName: {{ .controllerName }} + {{- end }} + {{- end }} \ No newline at end of file diff --git a/charts/airlock/microgateway/4.4.0/templates/operator/dashboard-configmap.yaml b/charts/airlock/microgateway/4.4.0/templates/operator/dashboard-configmap.yaml new file mode 100644 index 000000000..b71ac89b6 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/templates/operator/dashboard-configmap.yaml @@ -0,0 +1,28 @@ +{{- if .Values.dashboards.create -}} +{{- range $instance := (keys .Values.dashboards.instances | sortAlpha) -}} +{{- $dashboard := get $.Values.dashboards.instances $instance -}} +{{- if $dashboard.create }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "airlock-microgateway.fullname" $ }}-dashboard-{{ $instance | lower }} + namespace: {{ $.Release.Namespace }} + labels: + {{- include "airlock-microgateway.operator.labels" $ | nindent 4 }} + {{- with $.Values.dashboards.config.grafana.dashboardLabel -}} + {{- .name | nindent 4 -}}: {{ .value | quote }} + {{- end }} + annotations: + {{- with $.Values.dashboards.config.grafana.folderAnnotation -}} + {{- .name | nindent 4 -}}: {{ .value | quote }} + {{- end }} + {{- with $.Values.commonAnnotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +data: + {{- printf "%s.json" $instance | nindent 2 }}: |- + {{- ($.Files.Get (printf "dashboards/%s.json" $instance)) | nindent 4 -}} +{{- end -}} +{{- end -}} +{{- end -}} \ No newline at end of file diff --git a/charts/airlock/microgateway/4.4.0/templates/operator/deployment.yaml b/charts/airlock/microgateway/4.4.0/templates/operator/deployment.yaml new file mode 100644 index 000000000..db340cdec --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/templates/operator/deployment.yaml @@ -0,0 +1,143 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "airlock-microgateway.operator.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "airlock-microgateway.operator.labels" . | nindent 4 }} + {{- with .Values.commonAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + replicas: {{ .Values.operator.replicaCount }} + {{- with .Values.operator.updateStrategy }} + strategy: + {{- toYaml . | trim | nindent 4 }} + {{- end }} + selector: + matchLabels: + {{- include "airlock-microgateway.operator.selectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + checksum/config: {{ include (print $.Template.BasePath "/operator/configmap.yaml") . | sha256sum }} + kubectl.kubernetes.io/default-container: manager + {{- with mustMerge .Values.operator.podAnnotations .Values.commonAnnotations}} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "airlock-microgateway.operator.labels" . | nindent 8 }} + {{- with .Values.operator.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + containers: + - args: + - --config=operator_config.yaml + env: + - name: ENGINE_IMAGE + value: {{ include "airlock-microgateway.image" .Values.engine.image }} + - name: NETWORK_VALIDATOR_IMAGE + value: {{ include "airlock-microgateway.image" .Values.networkValidator.image }} + - name: SESSION_AGENT_IMAGE + value: {{ include "airlock-microgateway.image" .Values.sessionAgent.image }} + - name: OPERATOR_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: OPERATOR_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + image: {{ include "airlock-microgateway.image" .Values.operator.image }} + imagePullPolicy: {{ .Values.operator.image.pullPolicy }} + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + timeoutSeconds: 5 + name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + - containerPort: 13377 + name: xds-server + protocol: TCP + - containerPort: 8080 + protocol: TCP + - containerPort: 8081 + protocol: TCP + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 + {{- with .Values.operator.resources }} + resources: + {{- toYaml . | nindent 10 }} + {{- end }} + securityContext: + {{- include "airlock-microgateway.restrictedSecurityContext" . | nindent 10 }} + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: FallbackToLogsOnError + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true + - mountPath: /opt/airlock/license/ + name: airlock-microgateway-license + readOnly: true + - mountPath: /operator_config.yaml + name: operator-config + subPath: operator_config.yaml + - mountPath: /sidecar/engine_container_template.yaml + name: operator-config + subPath: engine_container_template.yaml + - mountPath: /sidecar/network_validator_container_template.yaml + name: operator-config + subPath: network_validator_container_template.yaml + - mountPath: /sidecar/session_agent_container_template.yaml + name: operator-config + subPath: session_agent_container_template.yaml + - mountPath: /engine_bootstrap_config_template.yaml + name: operator-config + subPath: engine_bootstrap_config_template.yaml + securityContext: + runAsNonRoot: true + serviceAccountName: {{ include "airlock-microgateway.operator.serviceAccountName" . }} + terminationGracePeriodSeconds: 10 + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.operator.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.operator.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.operator.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: {{ include "airlock-microgateway.operator.fullname" . }}-webhook-server-cert + - name: airlock-microgateway-license + secret: + defaultMode: 292 + optional: true + secretName: {{ .Values.license.secretName }} + - configMap: + name: {{ include "airlock-microgateway.operator.fullname" . }}-config + name: operator-config diff --git a/charts/airlock/microgateway/4.4.0/templates/operator/manager-role.yaml b/charts/airlock/microgateway/4.4.0/templates/operator/manager-role.yaml new file mode 100644 index 000000000..90335bcfe --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/templates/operator/manager-role.yaml @@ -0,0 +1,33 @@ +{{- if .Values.operator.rbac.create }} +{{- if empty .Values.operator.watchNamespaces }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "airlock-microgateway.operator.fullname" . }}-manager-{{ .Release.Namespace }} + labels: + {{- include "airlock-microgateway.operator.labels" . | nindent 4 }} + {{- with .Values.commonAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +rules: +{{ include "airlock-microgateway-operator.rbacRules" . -}} +{{- else }} +{{- range $namespace := (append .Values.operator.watchNamespaces .Release.Namespace | uniq) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "airlock-microgateway.operator.fullname" $ }}-manager + namespace: {{ $namespace }} + labels: + {{- include "airlock-microgateway.operator.labels" $ | nindent 4 }} + {{- with $.Values.commonAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +rules: +{{ include "airlock-microgateway-operator.rbacRules" $ }} +--- +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/airlock/microgateway/4.4.0/templates/operator/manager-rolebinding.yaml b/charts/airlock/microgateway/4.4.0/templates/operator/manager-rolebinding.yaml new file mode 100644 index 000000000..ae99cfb7b --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/templates/operator/manager-rolebinding.yaml @@ -0,0 +1,45 @@ +{{- if .Values.operator.rbac.create }} +{{- if empty .Values.operator.watchNamespaces }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "airlock-microgateway.operator.fullname" . }}-manager-{{ .Release.Namespace }} + labels: + {{- include "airlock-microgateway.operator.labels" . | nindent 4 }} + {{- with .Values.commonAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "airlock-microgateway.operator.fullname" . }}-manager-{{ .Release.Namespace }} +subjects: + - kind: ServiceAccount + name: {{ include "airlock-microgateway.operator.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +{{- else }} +{{- range $namespace := (append .Values.operator.watchNamespaces .Release.Namespace | uniq) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "airlock-microgateway.operator.fullname" $ }}-manager + namespace: {{ $namespace }} + labels: + {{- include "airlock-microgateway.operator.labels" $ | nindent 4 }} + {{- with $.Values.commonAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "airlock-microgateway.operator.fullname" $ }}-manager +subjects: + - kind: ServiceAccount + name: {{ include "airlock-microgateway.operator.serviceAccountName" $ }} + namespace: {{ $.Release.Namespace }} +--- +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/airlock/microgateway/4.4.0/templates/operator/metrics-service.yaml b/charts/airlock/microgateway/4.4.0/templates/operator/metrics-service.yaml new file mode 100644 index 000000000..34d23f6d6 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/templates/operator/metrics-service.yaml @@ -0,0 +1,47 @@ +apiVersion: v1 +kind: Service +metadata: + name: airlock-microgateway-operator-metrics + namespace: {{ .Release.Namespace }} + labels: + {{- include "airlock-microgateway.operator.labels" . | nindent 4 }} + {{- with .Values.operator.serviceLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with mustMerge .Values.operator.serviceAnnotations .Values.commonAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + ports: + - appProtocol: http + name: metrics + port: 8080 + protocol: TCP + selector: + {{- include "airlock-microgateway.operator.selectorLabels" . | nindent 4 }} +--- +apiVersion: v1 +kind: Service +metadata: + name: airlock-microgateway-operator-leader-metrics + namespace: {{ .Release.Namespace }} + labels: + {{- include "airlock-microgateway.operator.labels" . | nindent 4 }} + {{- with .Values.operator.serviceLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} + operator.microgateway.airlock.com/isLeader: "true" + {{- with mustMerge .Values.operator.serviceAnnotations .Values.commonAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + ports: + - appProtocol: http + name: metrics + port: 8080 + protocol: TCP + selector: + {{- include "airlock-microgateway.operator.selectorLabels" . | nindent 4 }} + operator.microgateway.airlock.com/isLeader: "true" \ No newline at end of file diff --git a/charts/airlock/microgateway/4.4.0/templates/operator/mutating-webhook.yaml b/charts/airlock/microgateway/4.4.0/templates/operator/mutating-webhook.yaml new file mode 100644 index 000000000..311f9726a --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/templates/operator/mutating-webhook.yaml @@ -0,0 +1,28 @@ +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + name: {{ include "airlock-microgateway.operator.fullname" . }}-webhook-{{ .Release.Namespace }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "airlock-microgateway.operator.labels" . | nindent 4 }} + annotations: + cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ include "airlock-microgateway.operator.fullname" . }}-serving-cert + {{- with .Values.commonAnnotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +webhooks: +{{- range $webhook := (include "airlock-microgateway-operator.mutatingWebhooks" .) | fromYamlArray }} +- {{ toYaml $webhook | indent 2 | trim }} + {{- with $.Values.operator.watchNamespaceSelector }} + namespaceSelector: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with $.Values.operator.watchNamespaces }} + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: In + values: + {{- toYaml . | nindent 10 }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/airlock/microgateway/4.4.0/templates/operator/podmonitor.yaml b/charts/airlock/microgateway/4.4.0/templates/operator/podmonitor.yaml new file mode 100644 index 000000000..1fe34fcb3 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/templates/operator/podmonitor.yaml @@ -0,0 +1,27 @@ +{{- if .Values.engine.sidecar.podMonitor.create }} +apiVersion: monitoring.coreos.com/v1 +kind: PodMonitor +metadata: + name: {{ include "airlock-microgateway.fullname" . }}-engine + namespace: {{ .Release.Namespace }} + labels: + {{- include "airlock-microgateway.operator.labels" . | nindent 4 }} + {{- with .Values.engine.sidecar.podMonitor.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.commonAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + namespaceSelector: + any: true + selector: + matchLabels: + sidecar.microgateway.airlock.com/inject: "true" + microgateway.airlock.com/managedBy: {{ .Release.Namespace }} + podMetricsEndpoints: + - targetPort: 19002 + path: /metrics + scheme: http +{{- end -}} \ No newline at end of file diff --git a/charts/airlock/microgateway/4.4.0/templates/operator/role.yaml b/charts/airlock/microgateway/4.4.0/templates/operator/role.yaml new file mode 100644 index 000000000..5378be8ef --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/templates/operator/role.yaml @@ -0,0 +1,45 @@ +{{- if .Values.operator.rbac.create -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "airlock-microgateway.operator.fullname" . }}-leader-election + namespace: {{ .Release.Namespace }} + labels: + {{- include "airlock-microgateway.operator.labels" . | nindent 4 }} + {{- with .Values.commonAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +rules: + - apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +{{- end -}} \ No newline at end of file diff --git a/charts/airlock/microgateway/4.4.0/templates/operator/rolebinding.yaml b/charts/airlock/microgateway/4.4.0/templates/operator/rolebinding.yaml new file mode 100644 index 000000000..bafec1015 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/templates/operator/rolebinding.yaml @@ -0,0 +1,20 @@ +{{- if .Values.operator.rbac.create -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "airlock-microgateway.operator.fullname" . }}-leader-election + namespace: {{ .Release.Namespace }} + labels: + {{- include "airlock-microgateway.operator.labels" . | nindent 4 }} + {{- with .Values.commonAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "airlock-microgateway.operator.fullname" . }}-leader-election +subjects: + - kind: ServiceAccount + name: {{ include "airlock-microgateway.operator.serviceAccountName" . }} +{{- end -}} diff --git a/charts/airlock/microgateway/4.4.0/templates/operator/selfsigned-issuer.yaml b/charts/airlock/microgateway/4.4.0/templates/operator/selfsigned-issuer.yaml new file mode 100644 index 000000000..466c56338 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/templates/operator/selfsigned-issuer.yaml @@ -0,0 +1,13 @@ +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: {{ include "airlock-microgateway.operator.fullname" . }}-selfsigned-issuer + namespace: {{ .Release.Namespace }} + labels: + {{- include "airlock-microgateway.operator.labels" . | nindent 4 }} + {{- with .Values.commonAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + selfSigned: {} diff --git a/charts/airlock/microgateway/4.4.0/templates/operator/serviceaccount.yaml b/charts/airlock/microgateway/4.4.0/templates/operator/serviceaccount.yaml new file mode 100644 index 000000000..434d7e9d3 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/templates/operator/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.operator.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "airlock-microgateway.operator.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "airlock-microgateway.operator.labels" . | nindent 4 }} + {{- with mustMerge .Values.operator.serviceAccount.annotations .Values.commonAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end -}} diff --git a/charts/airlock/microgateway/4.4.0/templates/operator/servicemonitor.yaml b/charts/airlock/microgateway/4.4.0/templates/operator/servicemonitor.yaml new file mode 100644 index 000000000..ff85a9a31 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/templates/operator/servicemonitor.yaml @@ -0,0 +1,60 @@ +{{- if .Values.operator.serviceMonitor.create }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "airlock-microgateway.operator.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "airlock-microgateway.operator.labels" . | nindent 4 }} + {{- with .Values.operator.serviceMonitor.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.commonAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + selector: + matchLabels: + {{- include "airlock-microgateway.operator.selectorLabels" . | nindent 6 }} + matchExpressions: + - { key: "operator.microgateway.airlock.com/isLeader", operator: DoesNotExist } + endpoints: + - path: /metrics + port: metrics + scheme: http + metricRelabelings: + - sourceLabels: + - __name__ + regex: {{ include "airlock-microgateway.operator.metricsLeaderOnlyRegexPattern" . }} + action: drop +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "airlock-microgateway.operator.fullname" . }}-leader + namespace: {{ .Release.Namespace }} + labels: + {{- include "airlock-microgateway.operator.labels" . | nindent 4 }} + {{- with .Values.operator.serviceMonitor.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.commonAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + selector: + matchLabels: + {{- include "airlock-microgateway.operator.selectorLabels" . | nindent 6 }} + operator.microgateway.airlock.com/isLeader: "true" + endpoints: + - path: /metrics + port: metrics + scheme: http + metricRelabelings: + - sourceLabels: + - __name__ + regex: {{ include "airlock-microgateway.operator.metricsLeaderOnlyRegexPattern" . }} + action: keep +{{- end -}} \ No newline at end of file diff --git a/charts/airlock/microgateway/4.4.0/templates/operator/serving-certificate.yaml b/charts/airlock/microgateway/4.4.0/templates/operator/serving-certificate.yaml new file mode 100644 index 000000000..60b92e1e2 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/templates/operator/serving-certificate.yaml @@ -0,0 +1,19 @@ +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: {{ include "airlock-microgateway.operator.fullname" . }}-serving-cert + namespace: {{ .Release.Namespace }} + labels: + {{- include "airlock-microgateway.operator.labels" . | nindent 4 }} + {{- with .Values.commonAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + dnsNames: + - airlock-microgateway-operator-webhook.{{ .Release.Namespace }}.svc + - airlock-microgateway-operator-webhook.{{ .Release.Namespace }}.svc.cluster.local + issuerRef: + kind: Issuer + name: {{ include "airlock-microgateway.operator.fullname" . }}-selfsigned-issuer + secretName: {{ include "airlock-microgateway.operator.fullname" . }}-webhook-server-cert diff --git a/charts/airlock/microgateway/4.4.0/templates/operator/validating-webhook.yaml b/charts/airlock/microgateway/4.4.0/templates/operator/validating-webhook.yaml new file mode 100644 index 000000000..5d6b4396b --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/templates/operator/validating-webhook.yaml @@ -0,0 +1,28 @@ +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: {{ include "airlock-microgateway.operator.fullname" . }}-webhook-{{ .Release.Namespace }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "airlock-microgateway.operator.labels" . | nindent 4 }} + annotations: + cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ include "airlock-microgateway.operator.fullname" . }}-serving-cert + {{- with .Values.commonAnnotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +webhooks: +{{- range $webhook := (include "airlock-microgateway-operator.validatingWebhooks" .) | fromYamlArray }} +- {{ toYaml $webhook | indent 2 | trim }} + {{- with $.Values.operator.watchNamespaceSelector }} + namespaceSelector: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with $.Values.operator.watchNamespaces }} + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: In + values: + {{- toYaml . | nindent 10 }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/airlock/microgateway/4.4.0/templates/operator/webhook-service.yaml b/charts/airlock/microgateway/4.4.0/templates/operator/webhook-service.yaml new file mode 100644 index 000000000..477ea839f --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/templates/operator/webhook-service.yaml @@ -0,0 +1,23 @@ +apiVersion: v1 +kind: Service +metadata: + name: airlock-microgateway-operator-webhook + namespace: {{ .Release.Namespace }} + labels: + {{- include "airlock-microgateway.operator.labels" . | nindent 4 }} + {{- with .Values.operator.serviceLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with mustMerge .Values.operator.serviceAnnotations .Values.commonAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + ports: + - appProtocol: https + name: webhook + port: 443 + protocol: TCP + targetPort: 9443 + selector: + {{- include "airlock-microgateway.operator.selectorLabels" . | nindent 4 }} \ No newline at end of file diff --git a/charts/airlock/microgateway/4.4.0/templates/operator/xds-service.yaml b/charts/airlock/microgateway/4.4.0/templates/operator/xds-service.yaml new file mode 100644 index 000000000..81b41acf5 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/templates/operator/xds-service.yaml @@ -0,0 +1,24 @@ +apiVersion: v1 +kind: Service +metadata: + name: airlock-microgateway-operator-xds + namespace: {{ .Release.Namespace }} + labels: + {{- include "airlock-microgateway.operator.labels" . | nindent 4 }} + {{- with .Values.operator.serviceLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with mustMerge .Values.operator.serviceAnnotations .Values.commonAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + ports: + - appProtocol: grpc + name: xds + port: 13377 + protocol: TCP + targetPort: 13377 + selector: + {{- include "airlock-microgateway.operator.selectorLabels" . | nindent 4 }} + operator.microgateway.airlock.com/isLeader: "true" diff --git a/charts/airlock/microgateway/4.4.0/templates/tests/rbac.yaml b/charts/airlock/microgateway/4.4.0/templates/tests/rbac.yaml new file mode 100644 index 000000000..93bd4cd1b --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/templates/tests/rbac.yaml @@ -0,0 +1,143 @@ +{{- if .Values.tests.enabled -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/component: tests + app.kubernetes.io/name: {{ include "airlock-microgateway.name" . }}-tests + {{- include "airlock-microgateway.sharedLabels" . | nindent 4 }} + name: "{{ include "airlock-microgateway.fullname" . }}-tests" + namespace: {{ .Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + app.kubernetes.io/component: tests + app.kubernetes.io/name: {{ include "airlock-microgateway.name" . }}-tests + {{- include "airlock-microgateway.sharedLabels" . | nindent 4 }} + name: "{{ include "airlock-microgateway.fullname" . }}-tests" + namespace: {{ .Release.Namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: "{{ include "airlock-microgateway.fullname" . }}-tests" +subjects: +- kind: ServiceAccount + name: "{{ include "airlock-microgateway.fullname" . }}-tests" +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app.kubernetes.io/component: tests + app.kubernetes.io/name: {{ include "airlock-microgateway.name" . }}-tests + {{- include "airlock-microgateway.sharedLabels" . | nindent 4 }} + name: "{{ include "airlock-microgateway.fullname" . }}-tests" + namespace: {{ .Release.Namespace }} +rules: +- apiGroups: + - microgateway.airlock.com + resources: + - sidecargateways + resourceNames: + - "{{ include "airlock-microgateway.fullname" . }}-test-sidecargateway" + verbs: + - get + - list + - watch + - delete +- apiGroups: + - microgateway.airlock.com + resources: + - sidecargateways + verbs: + - create +- apiGroups: + - "" + resources: + - events + verbs: + - list +- apiGroups: + - "apps" + resources: + - deployments + resourceNames: + - "{{ include "airlock-microgateway.operator.fullname" . }}" + verbs: + - get + - list + - watch +- apiGroups: + - "apps" + resources: + - statefulsets + - statefulsets/scale + resourceNames: + - "{{ include "airlock-microgateway.fullname" . }}-test-backend" + verbs: + - get + - list + - watch + - patch +- apiGroups: + - "" + resources: + - pods + - pods/log + - pods/status + - pods/attach + resourceNames: + - "{{ include "airlock-microgateway.fullname" . }}-test-backend-0" + - "{{ include "airlock-microgateway.fullname" . }}-test-valid-request" + - "{{ include "airlock-microgateway.fullname" . }}-test-injection-request" + verbs: + - get + - list + - create + - watch + - delete +- apiGroups: + - "" + resources: + - pods + verbs: + - create +{{- if .Values.operator.watchNamespaceSelector }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/component: tests + app.kubernetes.io/name: {{ include "airlock-microgateway.name" . }}-tests + {{- include "airlock-microgateway.sharedLabels" . | nindent 4 }} + name: "{{ include "airlock-microgateway.fullname" . }}-tests-{{ .Release.Namespace }}" +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: "{{ include "airlock-microgateway.fullname" . }}-tests-{{ .Release.Namespace }}" +subjects: + - kind: ServiceAccount + name: "{{ include "airlock-microgateway.fullname" . }}-tests" + namespace: {{ .Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/component: tests + app.kubernetes.io/name: {{ include "airlock-microgateway.name" . }}-tests + {{- include "airlock-microgateway.sharedLabels" . | nindent 4 }} + name: "{{ include "airlock-microgateway.fullname" . }}-tests-{{ .Release.Namespace }}" +rules: +- apiGroups: + - "" + resources: + - namespaces + verbs: + - get + - list +{{- end }} +{{- end -}} diff --git a/charts/airlock/microgateway/4.4.0/templates/tests/service.yaml b/charts/airlock/microgateway/4.4.0/templates/tests/service.yaml new file mode 100644 index 000000000..30ddc278d --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/templates/tests/service.yaml @@ -0,0 +1,23 @@ +{{- if .Values.tests.enabled -}} +apiVersion: v1 +kind: Service +metadata: + name: "{{ include "airlock-microgateway.fullname" . }}-test-service" + namespace: {{ .Release.Namespace }} + labels: + app: test-service + app.kubernetes.io/component: test-install + app.kubernetes.io/name: {{ include "airlock-microgateway.name" . }}-tests + {{- include "airlock-microgateway.sharedLabels" . | nindent 4 }} + {{- include "airlock-microgateway.sharedSelectorLabels" . | nindent 4 }} +spec: + selector: + app.kubernetes.io/component: test-install + app.kubernetes.io/name: {{ include "airlock-microgateway.name" . }}-tests + app: "{{ include "airlock-microgateway.fullname" . }}-test-backend" + {{- include "airlock-microgateway.sharedSelectorLabels" . | nindent 4 }} + ports: + - name: http + port: 8080 + targetPort: 8080 +{{- end -}} \ No newline at end of file diff --git a/charts/airlock/microgateway/4.4.0/templates/tests/statefulset.yaml b/charts/airlock/microgateway/4.4.0/templates/tests/statefulset.yaml new file mode 100644 index 000000000..710a7b9f6 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/templates/tests/statefulset.yaml @@ -0,0 +1,56 @@ +{{- if .Values.tests.enabled -}} +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: "{{ include "airlock-microgateway.fullname" . }}-test-backend" + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/component: test-install + app.kubernetes.io/name: {{ include "airlock-microgateway.name" . }}-tests + app: "{{ include "airlock-microgateway.fullname" . }}-test-backend" + {{- include "airlock-microgateway.sharedLabels" . | nindent 4 }} + {{- include "airlock-microgateway.sharedSelectorLabels" . | nindent 4 }} +spec: + serviceName: nginx + replicas: 0 + selector: + matchLabels: + app.kubernetes.io/component: test-install + app.kubernetes.io/name: {{ include "airlock-microgateway.name" . }}-tests + app: "{{ include "airlock-microgateway.fullname" . }}-test-backend" + {{- include "airlock-microgateway.sharedSelectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + k8s.v1.cni.cncf.io/networks: default/airlock-microgateway-cni + labels: + sidecar.microgateway.airlock.com/inject: "true" + sidecar.istio.io/inject: "false" + app.kubernetes.io/component: test-install + app.kubernetes.io/name: {{ include "airlock-microgateway.name" . }}-tests + app: "{{ include "airlock-microgateway.fullname" . }}-test-backend" + {{- include "airlock-microgateway.sharedLabels" . | nindent 8 }} + {{- include "airlock-microgateway.sharedSelectorLabels" . | nindent 8 }} + spec: + containers: + - image: cgr.dev/chainguard/nginx + name: nginx + ports: + - containerPort: 8080 + volumeMounts: + - mountPath: /var/lib/nginx/tmp/ + name: nginx-tmp + - mountPath: /var/run + name: nginx-run + securityContext: + {{- include "airlock-microgateway.restrictedSecurityContext" . | nindent 12 }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + - emptyDir: {} + name: nginx-tmp + - emptyDir: {} + name: nginx-run +{{- end -}} \ No newline at end of file diff --git a/charts/airlock/microgateway/4.4.0/templates/tests/test-install.yaml b/charts/airlock/microgateway/4.4.0/templates/tests/test-install.yaml new file mode 100644 index 000000000..721ae2b82 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/templates/tests/test-install.yaml @@ -0,0 +1,227 @@ +{{- if .Values.tests.enabled -}} +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "airlock-microgateway.fullname" . }}-test-install" + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/component: test-install + app.kubernetes.io/name: {{ include "airlock-microgateway.name" . }}-tests + sidecar.istio.io/inject: "false" + {{- include "airlock-microgateway.sharedLabels" . | nindent 4 }} + {{- include "airlock-microgateway.sharedSelectorLabels" . | nindent 4 }} + annotations: + helm.sh/hook: test + helm.sh/hook-delete-policy: before-hook-creation +spec: + restartPolicy: Never + containers: + - name: test + image: "bitnami/kubectl:{{ .Capabilities.KubeVersion.Major }}.{{ .Capabilities.KubeVersion.Minor }}" + securityContext: + {{- include "airlock-microgateway.restrictedSecurityContext" . | nindent 6 }} + command: + - sh + - -c + - | + set -eu + + clean_up() { + echo "" + echo "### Clean up test resources" + kubectl delete --ignore-not-found=true -n {{ .Release.Namespace }} sidecargateways.microgateway.airlock.com {{ include "airlock-microgateway.fullname" . }}-test-sidecargateway || true + echo "" + echo "### Scale down '{{ include "airlock-microgateway.fullname" . }}-test-backend'" + kubectl scale -n {{ .Release.Namespace }} statefulset/{{ include "airlock-microgateway.fullname" . }}-test-backend --replicas=0 --timeout=60s + sleep 3s + echo "" + } + + fail() { + echo "" + echo "### Error: ${1}" + echo "" + + if kubectl get -n {{ .Release.Namespace }} sidecargateway.microgateway.airlock.com/{{ include "airlock-microgateway.fullname" . }}-test-sidecargateway >/dev/null 2>&1; then + echo "" + echo 'Microgateway Sidecargateway status:' + kubectl get -n {{ .Release.Namespace }} sidecargateway.microgateway.airlock.com/{{ include "airlock-microgateway.fullname" . }}-test-sidecargateway -o jsonpath-as-json='{.status}' || true + echo "" + echo "" + fi + + if kubectl get -n {{ .Release.Namespace }} pods/{{ include "airlock-microgateway.fullname" . }}-test-backend-0 >/dev/null 2>&1; then + echo "Pod '{{ include "airlock-microgateway.fullname" . }}-test-backend-0':" + kubectl describe -n {{ .Release.Namespace }} pods/{{ include "airlock-microgateway.fullname" . }}-test-backend-0 || true + echo "" + echo "" + echo 'Logs of Nginx container:' + kubectl logs -n {{ .Release.Namespace }} pods/{{ include "airlock-microgateway.fullname" . }}-test-backend-0 -c nginx --tail 5 || true + echo "" + echo "" + # Wait for engine logs + sleep 10s + echo 'Logs of Microgateway Engine container:' + kubectl logs -n {{ .Release.Namespace }} pods/{{ include "airlock-microgateway.fullname" . }}-test-backend-0 -c airlock-microgateway-engine --tail 5 || true + fi + + exit 1 + } + + create_sidecargateway() { + # create SidecarGateway resource for testing purposes + kubectl delete --ignore-not-found=true -n {{ .Release.Namespace }} sidecargateways.microgateway.airlock.com {{ include "airlock-microgateway.fullname" . }}-test-sidecargateway || true + kubectl apply -f - </dev/null 2>&1; do sleep 1s; i=$((i+1)); done + kubectl logs -f -n {{ .Release.Namespace }} {{ include "airlock-microgateway.fullname" . }}-test-valid-request + kubectl delete pod --ignore-not-found=true -n {{ .Release.Namespace }} {{ include "airlock-microgateway.fullname" . }}-test-valid-request + } + + {{- if .Values.operator.watchNamespaceSelector }} + echo "### Verify that Namespace Selector matches Namespace '{{ .Release.Namespace }}'" + if ! kubectl get namespace -l '{{ include "airlock-microgateway.watchNamespaceSelector.labelQuery" .Values.operator.watchNamespaceSelector }}' | grep -q {{ .Release.Namespace }}; then + labels=$(kubectl get namespace {{ .Release.Namespace }} -o jsonpath={.metadata.labels} | jq | awk '{print " " $0}') + fail {{printf `"Operator namespace '%s' is not part of the operator's watch scope. To execute 'helm test', the selector configured in the helm value 'operator.watchNamespaceSelector' must match the namespace's labels:\n* Current selector:\n%s\n\n* Current labels:\n$labels\n###"` + .Release.Namespace + (replace "\"" "\\\"" (replace "\n" "\\n" (.Values.operator.watchNamespaceSelector | toPrettyJson | indent 2))) + }} + fi + echo "" + {{- end }} + + trap clean_up EXIT + echo "" + + echo "### Waiting for Microgateway Operator Deployments to be ready" + if ! kubectl rollout status -n {{ .Release.Namespace }} --timeout=90s \ + deployments/{{ include "airlock-microgateway.operator.fullname" . }}; then + fail 'Timeout occurred' + fi + echo "" + + echo "### Scale '{{ include "airlock-microgateway.fullname" . }}-test-backend' to '1' replica" + # scale to zero replicas to ensure no pods are present from previous runs + kubectl scale -n {{ .Release.Namespace }} statefulset/{{ include "airlock-microgateway.fullname" . }}-test-backend --replicas=0 --timeout=10s + kubectl scale -n {{ .Release.Namespace }} statefulset/{{ include "airlock-microgateway.fullname" . }}-test-backend --replicas=1 --timeout=10s + echo "" + + echo "### Waiting for backend pod" + i=0 + while true; do + if kubectl get -n {{ .Release.Namespace }} pods/{{ include "airlock-microgateway.fullname" . }}-test-backend-0; then + break + elif [ $i -gt 3 ]; then + fail 'Pod not ready' + fi + sleep 2s + i=$((i+1)) + done + + echo "### Checking Microgateway Engine sidecar container was injected" + if ! kubectl get -n {{ .Release.Namespace }} pods/{{ include "airlock-microgateway.fullname" . }}-test-backend-0 -o jsonpath='{.spec.containers[?(@.name=="airlock-microgateway-engine")]}' | grep -q "airlock-microgateway-engine"; then + fail 'Microgateway Engine sidecar container not injected' + fi + echo "True" + echo "" + + echo "### Checking for valid license" + i=0 + while true; do + if [ "$(kubectl get -n {{ .Release.Namespace }} pods/{{ include "airlock-microgateway.fullname" . }}-test-backend-0 -o jsonpath='{.metadata.labels.sidecar\.microgateway\.airlock\.com/licensed}')" = 'true' ]; then + break + elif [ $i -gt 30 ]; then + fail 'Microgateway license is missing or invalid' + fi + sleep 2s + i=$((i+1)) + done + echo "True" + echo "" + + echo "### Create SidecarGateway resource for testing" + if ! create_sidecargateway ; then + fail 'Creation of SidecarGateway resource failed' + fi + echo "" + + echo "### Waiting for '{{ include "airlock-microgateway.fullname" . }}-test-backend' to be ready" + if ! kubectl rollout status -n {{ .Release.Namespace }} statefulset/{{ include "airlock-microgateway.fullname" . }}-test-backend --timeout=90s; then + fail 'Timeout occurred' + fi + echo "" + + echo "### Waiting for 'engine-config-valid' condition" + if ! kubectl wait -n {{ .Release.Namespace }} pods --field-selector=metadata.name={{ include "airlock-microgateway.fullname" . }}-test-backend-0 --timeout=90s --for=condition=microgateway.airlock.com/engine-config-valid=True; then + fail 'Configuration was never accepted by the Microgateway Engine' + fi + sleep 5s + echo "" + echo "" + + echo "### Checking whether a valid request is successful and returns HTTP status code '200'" + out=$(curl -vsS --retry 3 --retry-connrefused --connect-timeout 10 "http://{{ include "airlock-microgateway.fullname" . }}-test-service:8080/" || true) + echo "Response:" + echo "${out}" + if ! echo "${out}" | grep -q "200 OK"; then + fail 'A valid request was not successful' + fi + echo "" + echo "" + + echo "### Checking whether a request with an injection attack is blocked and returns HTTP status code '400'" + out=$(curl -vsS --retry 3 --retry-connrefused --connect-timeout 10 "http://{{ include "airlock-microgateway.fullname" . }}-test-service:8080/?token='%20UnION%20all%20select%20A" || true) + echo "Response:" + echo "${out}" + if ! echo "${out}" | grep -q "400 Bad Request"; then + fail 'A malicious request was not blocked' + fi + echo "" + echo "" + + echo "### Installation of '{{ include "airlock-microgateway.fullname" . }}' succeeded" + exit 0 + serviceAccountName: "{{ include "airlock-microgateway.fullname" . }}-tests" +{{- end -}} diff --git a/charts/airlock/microgateway/4.4.0/values.schema.json b/charts/airlock/microgateway/4.4.0/values.schema.json new file mode 100644 index 000000000..05c7d7717 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/values.schema.json @@ -0,0 +1,572 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "nameOverride": { + "type": "string" + }, + "fullnameOverride": { + "type": "string" + }, + "commonLabels": { + "$ref": "#/definitions/StringMap" + }, + "commonAnnotations": { + "$ref": "#/definitions/StringMap" + }, + "crds": { + "type": "object", + "properties": { + "skipVersionCheck": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "imagePullSecrets": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "name" + ], + "additionalProperties": true + } + }, + "operator": { + "type": "object", + "properties": { + "replicaCount": { + "type": "integer", + "minimum": 0 + }, + "updateStrategy": { + "$ref": "#/definitions/UpdateStrategy" + }, + "image": { + "$ref": "#/definitions/Image" + }, + "podAnnotations": { + "$ref": "#/definitions/StringMap" + }, + "podLabels": { + "$ref": "#/definitions/StringMap" + }, + "serviceAnnotations": { + "$ref": "#/definitions/StringMap" + }, + "serviceLabels": { + "$ref": "#/definitions/StringMap" + }, + "resources": { + "type": "object" + }, + "nodeSelector": { + "$ref": "#/definitions/StringMap" + }, + "tolerations": { + "type": "array", + "items": { + "type": "object" + } + }, + "affinity": { + "type": "object" + }, + "config": { + "type": "object", + "properties": { + "logLevel": { + "type": "string", + "enum": [ + "debug", + "info", + "warn", + "error" + ] + } + }, + "required": [ + "logLevel" + ], + "additionalProperties": false + }, + "serviceAccount": { + "type": "object", + "properties": { + "create": { + "type": "boolean" + }, + "annotations": { + "$ref": "#/definitions/StringMap" + }, + "name": { + "type": "string" + } + }, + "required": [ + "annotations", + "create", + "name" + ], + "additionalProperties": false + }, + "watchNamespaces": { + "type": "array", + "items": { + "type": "string" + } + }, + "watchNamespaceSelector": { + "$ref": "#/definitions/LabelSelector" + }, + "rbac": { + "type": "object", + "properties": { + "create": { + "type": "boolean" + } + }, + "required": [ + "create" + ], + "additionalProperties": false + }, + "serviceMonitor": { + "type": "object", + "properties": { + "create": { + "type": "boolean" + }, + "labels": { + "$ref": "#/definitions/StringMap" + } + }, + "required": [ + "create" + ], + "additionalProperties": false + }, + "gatewayAPI": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "controllerName" : { + "type": "string", + "pattern": "^microgateway\\.airlock\\.com\/[A-Za-z0-9\/\\-._~%!$&'()*+,;=:]+$" + } + }, + "required": [ + "enabled" + ], + "additionalProperties": false + } + }, + "oneOf": [ + { + "properties": { + "watchNamespaces": { + "minItems": 1 + }, + "watchNamespaceSelector": { + "additionalProperties": false + } + } + }, + { + "properties": { + "watchNamespaces": { + "maxItems": 0 + }, + "watchNamespaceSelector": { + "$ref": "#/definitions/LabelSelector" + } + } + } + ], + "required": [ + "affinity", + "config", + "image", + "updateStrategy", + "nodeSelector", + "podAnnotations", + "podLabels", + "rbac", + "replicaCount", + "resources", + "serviceAccount", + "serviceAnnotations", + "serviceLabels", + "serviceMonitor", + "tolerations" + ], + "additionalProperties": false + }, + "engine": { + "type": "object", + "properties": { + "image": { + "$ref": "#/definitions/Image" + }, + "resources": { + "type": "object" + }, + "sidecar": { + "type": "object", + "properties":{ + "podMonitor": { + "type": "object", + "properties": { + "create": { + "type": "boolean" + }, + "labels": { + "$ref": "#/definitions/StringMap" + } + }, + "required": [ + "create" + ], + "additionalProperties": false + } + }, + "required": [ + "podMonitor" + ], + "additionalProperties": false + } + }, + "required": [ + "image", + "resources", + "sidecar" + ], + "additionalProperties": false + }, + "networkValidator": { + "type": "object", + "properties": { + "image": { + "$ref": "#/definitions/Image" + }, + "resources": { + "type": "object" + } + }, + "required": [ + "image", + "resources" + ], + "additionalProperties": false + }, + "sessionAgent": { + "type": "object", + "properties": { + "image": { + "$ref": "#/definitions/Image" + }, + "resources": { + "type": "object" + } + }, + "required": [ + "image", + "resources" + ], + "additionalProperties": false + }, + "license": { + "type": "object", + "properties": { + "secretName": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "secretName" + ], + "additionalProperties": false + }, + "dashboards": { + "type": "object", + "properties" : { + "create": { + "type": "boolean" + }, + "config": { + "type": "object", + "properties": { + "grafana": { + "type": "object", + "properties": { + "folderAnnotation": { + "$ref": "#/definitions/NameValuePair" + }, + "dashboardLabel": { + "$ref": "#/definitions/NameValuePair" + } + }, + "required": [ + "folderAnnotation", + "dashboardLabel" + ], + "additionalProperties": false + } + }, + "required": [ + "grafana" + ], + "additionalProperties": false + }, + "instances": { + "type": "object", + "properties": { + "overview": { + "$ref": "#/definitions/DashboardInstance" + }, + "license" : { + "$ref": "#/definitions/DashboardInstance" + }, + "blockMetrics" : { + "$ref": "#/definitions/DashboardInstance" + }, + "blockLogs" : { + "$ref": "#/definitions/DashboardInstance" + }, + "headerLogs" : { + "$ref": "#/definitions/DashboardInstance" + }, + "logOnlyMetrics" : { + "$ref": "#/definitions/DashboardInstance" + }, + "logOnlyLogs" : { + "$ref": "#/definitions/DashboardInstance" + } + }, + "required": [ + "overview", + "license", + "blockMetrics", + "blockLogs", + "headerLogs", + "logOnlyMetrics", + "logOnlyLogs" + ], + "additionalProperties": false + } + }, + "required": [ + "create", + "config", + "instances" + ], + "additionalProperties": false + }, + "tests": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + }, + "required": [ + "enabled" + ], + "additionalProperties": false + }, + "global": { + "type": "object" + } + }, + "required": [ + "commonAnnotations", + "commonLabels", + "crds", + "engine", + "fullnameOverride", + "imagePullSecrets", + "license", + "nameOverride", + "operator", + "networkValidator", + "sessionAgent", + "dashboards", + "tests" + ], + "additionalProperties": false, + "definitions": { + "StringMap": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "Image": { + "type": "object", + "properties": { + "repository": { + "type": "string", + "minLength": 1 + }, + "tag": { + "type": "string" + }, + "digest": { + "type": "string", + "pattern": "^$|^sha256:[a-f0-9]{64}$" + }, + "pullPolicy": { + "type": "string", + "enum": [ + "Always", + "IfNotPresent", + "Never" + ] + } + }, + "required": [ + "digest", + "pullPolicy", + "repository", + "tag" + ], + "additionalProperties": false + }, + "LabelSelector": { + "type": "object", + "properties": { + "matchExpressions": { + "type": "array", + "items": { + "type": "object", + "required": [ + "key", + "operator" + ], + "properties": { + "key": { + "type": "string" + }, + "operator": { + "type": "string" + }, + "values": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + } + }, + "matchLabels": { + "$ref": "#/definitions/StringMap" + } + }, + "additionalProperties": false + }, + "UpdateStrategy": { + "type": "object", + "oneOf" : [ + { + "properties": { + "type": { + "$ref": "#/definitions/RecreateType" + } + }, + "required": [ + "type" + ], + "additionalProperties": false + }, + { + "properties": { + "type": { + "$ref": "#/definitions/RollingUpdateType" + }, + "rollingUpdate": { + "$ref": "#/definitions/RollingUpdate" + } + }, + "required": [ + "type" + ], + "additionalProperties": false + } + ] + }, + "RecreateType": { + "type": "string", + "enum": [ + "Recreate" + ] + }, + "RollingUpdateType": { + "type": "string", + "enum": [ + "RollingUpdate" + ] + }, + "RollingUpdate": { + "type": "object", + "properties": { + "maxSurge": { + "type": ["integer", "string"], + "minimum": 0, + "pattern": "^\\d+%?$" + }, + "maxUnavailable": { + "type": ["integer", "string"], + "minimum": 0, + "pattern": "^\\d+%?$" + } + }, + "anyOf": [ + {"required": ["maxSurge"]}, + {"required": ["maxUnavailable"]} + ], + "additionalProperties": false + }, + "DashboardInstance" : { + "type" : "object", + "properties" : { + "create" : { + "type" : "boolean" + } + }, + "required" : [ + "create" + ], + "additionalProperties": false + }, + "NameValuePair" : { + "type" : "object", + "properties" : { + "name" : { + "type": "string", + "minLength": 1 + }, + "value" : { + "type" : "string", + "minLength": 1 + } + }, + "required" : [ + "name", + "value" + ], + "additionalProperties": false + } + } +} diff --git a/charts/airlock/microgateway/4.4.0/values.yaml b/charts/airlock/microgateway/4.4.0/values.yaml new file mode 100644 index 000000000..7d3504716 --- /dev/null +++ b/charts/airlock/microgateway/4.4.0/values.yaml @@ -0,0 +1,237 @@ +# -- Allows overriding the name to use instead of "microgateway". +nameOverride: "" +# -- Allows overriding the name to use as full name of resources. +fullnameOverride: "" +# -- Labels to add to all resources. +commonLabels: {} +# -- Annotations to add to all resources. +commonAnnotations: {} +# -- ImagePullSecrets to use when pulling images. +imagePullSecrets: [] +# - name: myRegistryKeySecretName + +crds: + # -- Whether to skip the sanity check which prevents installing/upgrading the helm chart in a cluster with outdated Airlock Microgateway CRDs. + # The check aims to prevent unexpected behavior and issues due to Helm v3 not automatically upgrading CRDs which are already present in the cluster + # when performing a "helm install/upgrade". + skipVersionCheck: false +operator: + # -- Number of replicas for the operator Deployment. + replicaCount: 2 + # -- Specifies the operator update strategy. + updateStrategy: + type: RollingUpdate + # Specifies the Airlock Microgateway Operator image. + image: + # -- Image repository from which to pull the Airlock Microgateway Operator image. + repository: "quay.io/airlock/microgateway-operator" + # -- Image tag to pull. + tag: "4.4.0" + # -- SHA256 image digest to pull (in the format "sha256:c79ee3f85862fb386e9dd62b901b607161d27807f512d7fbdece05e9ee3d7c63"). + # Overrides tag when specified. + digest: "sha256:80cbae58ad9badd9395fa09a7b0576561821121b8353146bbd6efa2240ab5d97" + # -- Pull policy for this image. + pullPolicy: IfNotPresent + # -- Annotations to add to all Pods. + podAnnotations: {} + # -- Labels to add to all Pods. + podLabels: {} + # -- Annotations to add to the Service. + serviceAnnotations: {} + # prometheus.io/scrape: "true" + # prometheus.io/port: "8080" + + # -- Labels to add to the Service. + serviceLabels: {} + # -- Resource restrictions to apply to the operator container. + resources: {} + # We recommend at least the following resource specification. + # limits: + # cpu: 1000m + # memory: 512Mi + # requests: + # cpu: 100m + # memory: 512Mi + + # -- Custom nodeSelector to apply to the operator Deployment in order to constrain its Pods to certain nodes. + nodeSelector: {} + # -- Custom tolerations to apply to the operator Deployment in order to allow its Pods to run on tainted nodes. + tolerations: [] + # -- Custom affinity to apply to the operator Deployment. Used to influence the scheduling. + affinity: {} + # Parameters for the operator configuration. + config: + # -- Operator application log level. + logLevel: "info" + # Configures the generation of the ServiceAccount. + serviceAccount: + # -- Whether a ServiceAccount should be created. + create: true + # -- Annotations to add to the ServiceAccount. + annotations: {} + # -- Name of the ServiceAccount to use. + # If not set and create is true, a name is generated using the fullname template. + name: "" + # -- Allows to restrict the operator to specific namespaces, depending on your needs. + # For a `OwnNamespace` or `SingleNamespace` installation the list may only contain one namespace (e.g., `watchNamespaces: ["airlock-microgateway-system"]`). + # In case of the `OwnNamespace` installation mode the specified namespace should be equal to the installation namespace. + # For a static `MultiNamespace` installation, the complete list of namespaces must be provided in the `watchNamespaces`. + # An `AllNamespaces` installation or the usage of the `watchNamespaceSelector` requires the `watchNamespaces` to be empty. + # Regardless of the installation modes supported by `watchNamespaces`, RBAC is created only namespace-scoped (using Roles and RoleBindings) in the respective namespaces. + # Please note that this feature requires a Premium license. + watchNamespaces: [] + # -- Allows to dynamically select watch namespaces of the operator and the scope of the webhooks based on a Namespace label selector. + # It is able to detect and reconcile resources in all namespaces that match the label selector automatically, even for new namespaces, without restarting the operator. + # This facilitates a dynamic `MultiNamespace` installation mode, but still requires cluster-scoped permissions (i.e., ClusterRoles and ClusterRoleBindings). + # An `AllNamespaces` installation or the usage of the `watchNamespaces` requires the `watchNamespaceSelector` to be empty. + # Please note that this feature requires a Premium license. + watchNamespaceSelector: {} + # For further examples, see: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#resources-that-support-set-based-requirements. + # matchLabels: + # microgateway.airlock.com/enable: "true" + # matchExpressions: + # - { key: environment, operator: NotIn, values: [dev] } + + # Configures the generation of Role and RoleBinding as well as ClusterRoles and ClusterRoleBinding pairs for the ServiceAccount specified above. + rbac: + # -- Whether to create RBAC resources which are required for the Airlock Microgateway Operator to function. + create: true + # Configures the generation of a Prometheus Operator ServiceMonitor. + serviceMonitor: + # -- Whether to create a ServiceMonitor resource for monitoring. + create: false + # -- Labels to add to the ServiceMonitor. + labels: {} + # release: "" + # Configures the Kubernetes Gateway API integration. + gatewayAPI: + # -- Whether to enable the Kubernetes Gateway API related controllers. + # Requires that the gateway.networking.k8s.io/v1 resources are installed on the cluster. + enabled: false + # -- Controller name referred in the GatewayClasses managed by this operator. The value must be a path prefixed by the domain `microgateway.airlock.com`. + controllerName: microgateway.airlock.com/gatewayclass-controller +engine: + # Specifies the Airlock Microgateway Engine image. + image: + # -- Image repository from which to pull the Airlock Microgateway Engine image. + repository: "quay.io/airlock/microgateway-engine" + # -- Image tag to pull. + tag: "4.4.0" + # -- SHA256 image digest to pull (in the format "sha256:a3051f42d3013813b05f7513bb86ed6a3209cb3003f1bb2f7b72df249aa544d3"). + # Overrides tag when specified. + digest: "sha256:c29adf07e7536b72447ea694d0e19fe19235306c26d412a9abc43e4dd99b84c8" + # -- Pull policy for this image. + pullPolicy: IfNotPresent + # -- Resource restrictions to apply to the Airlock Microgateway Engine container. + resources: {} + # We recommend at least the following resource specification. + # limits: + # cpu: 500m + # memory: 128Mi + # requests: + # cpu: 10m + # memory: 40Mi + + # Additional configuration when deployed as a sidecar. + sidecar: + # Configures the generation of a Prometheus Operator PodMonitor. + podMonitor: + # -- Whether to create a PodMonitor resource for monitoring. + create: false + # -- Labels to add to the PodMonitor. + labels: {} + # release: "" +networkValidator: + # Specifies the Airlock Microgateway Network Validator image to be injected as an init-container. + image: + # -- Image repository from which to pull the netcat image for the Airlock Microgateway Network Validator init-container. + repository: "cgr.dev/chainguard/netcat" + # -- Image tag to pull. + tag: "" + # -- SHA256 image digest to pull (in the format "sha256:05585644690678ae6453ab12e3a5f899e7be5ab70f56c6bf1c4484d3b53587d2"). + # Overrides tag when specified. + digest: "sha256:05585644690678ae6453ab12e3a5f899e7be5ab70f56c6bf1c4484d3b53587d2" + # -- Pull policy for this image. + pullPolicy: IfNotPresent + # -- Resource restrictions to apply to the Airlock Microgateway Network Validator init-container. + resources: + limits: + cpu: 25m + memory: 12Mi + requests: + cpu: 5m + memory: 1Mi +sessionAgent: + # Specifies the Airlock Microgateway Session Agent image. + image: + # -- Image repository from which to pull the Airlock Microgateway Session Agent image. + repository: "quay.io/airlock/microgateway-session-agent" + # -- Image tag to pull. + tag: "4.4.0" + # -- SHA256 image digest to pull (in the format "sha256:a3051f42d3013813b05f7513bb86ed6a3209cb3003f1bb2f7b72df249aa544d3"). + # Overrides tag when specified. + digest: "sha256:fbb90f2a52bb1b19cca6c5c133e80331153c019ec905db052c250fedbb09c3bc" + # -- Pull policy for this image. + pullPolicy: IfNotPresent + # -- Resource restrictions to apply to the Airlock Microgateway Session Agent container. + resources: {} + # We recommend at least the following resource specification. + # limits: + # cpu: 150m + # memory: 32Mi + # requests: + # cpu: 10m + # memory: 8Mi +license: + # -- Name of the secret containing the "microgateway-license.txt" key. + secretName: "airlock-microgateway-license" +# Creates dashboards in the form of ConfigMaps that can be imported +# by Grafana using its sidecar setup. +dashboards: + # -- Whether to create any ConfigMaps containing Grafana dashboards to import. + create: false + config: + # Configures the necessary label and annotations along with their values + # to enable Grafana to correctly identify the ConfigMaps containing + # dashboards and file them within a dedicated folder in the dashboard overview. + # These settings need to match the Grafana sidecar configuration. + grafana: + folderAnnotation: + # -- Name of the annotation containing the folder name to file dashboards into. + name: "grafana_folder" + # -- Name of the folder dashboards are filed into within the Grafana UI. + value: "Airlock Microgateway" + dashboardLabel: + # -- Name of the label that lets Grafana identify ConfigMaps that represent dashboards. + name: "grafana_dashboard" + # -- Value of the label that lets Grafana identify ConfigMaps that represent dashboards. + value: "1" + instances: + # Available dashboard instances that can be individually created/deployed. + overview: + # -- Whether to create the overview dashboard. + create: true + license: + # -- Whether to create the license dashboard. + create: true + blockMetrics: + # -- Whether to create the block metrics dashboard. + create: true + blockLogs: + # -- Whether to create the block logs dashboard. + create: true + headerLogs: + # -- Whether to create the header rewrite logs dashboard. + create: true + logOnlyMetrics: + # -- Whether to create the log only metrics dashboard + create: true + logOnlyLogs: + # -- Whether to create the log only logs dashboard. + create: true +# Check whether the installation of the Airlock Microgateway Helm Chart was successful. +# Requires a secret with a valid Airlock Microgateway license key already to be present. +tests: + # -- Whether additional resources required for running `helm test` should be created (e.g. Roles and ServiceAccounts). + # If set to false, `helm test` will not run any tests. + enabled: false diff --git a/charts/buoyant/linkerd-control-plane/2024.10.3/Chart.yaml b/charts/buoyant/linkerd-control-plane/2024.10.3/Chart.yaml index c37dae0e4..a5070baad 100644 --- a/charts/buoyant/linkerd-control-plane/2024.10.3/Chart.yaml +++ b/charts/buoyant/linkerd-control-plane/2024.10.3/Chart.yaml @@ -2,7 +2,6 @@ annotations: catalog.cattle.io/auto-install: linkerd-crds catalog.cattle.io/certified: partner catalog.cattle.io/display-name: Linkerd Control Plane - catalog.cattle.io/featured: "5" catalog.cattle.io/kube-version: '>=1.22.0-0' catalog.cattle.io/release-name: linkerd-control-plane apiVersion: v2 diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/.helmignore b/charts/buoyant/linkerd-control-plane/2024.10.4/.helmignore new file mode 100644 index 000000000..79c90a806 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/.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 +OWNERS +# 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/buoyant/linkerd-control-plane/2024.10.4/Chart.lock b/charts/buoyant/linkerd-control-plane/2024.10.4/Chart.lock new file mode 100644 index 000000000..a0cb7ec8c --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: partials + repository: file://../partials + version: 0.1.0 +digest: sha256:8e42f9c9d4a2dc883f17f94d6044c97518ced19ad0922f47b8760e47135369ba +generated: "2021-12-06T11:42:50.784240359-05:00" diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/Chart.yaml b/charts/buoyant/linkerd-control-plane/2024.10.4/Chart.yaml new file mode 100644 index 000000000..659610c98 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/Chart.yaml @@ -0,0 +1,29 @@ +annotations: + catalog.cattle.io/auto-install: linkerd-crds + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Linkerd Control Plane + catalog.cattle.io/featured: "5" + catalog.cattle.io/kube-version: '>=1.22.0-0' + catalog.cattle.io/release-name: linkerd-control-plane +apiVersion: v2 +appVersion: edge-24.10.4 +dependencies: +- name: partials + repository: file://../partials + version: 0.1.0 +description: 'Linkerd gives you observability, reliability, and security for your + microservices — with no code change required. ' +home: https://linkerd.io +icon: file://assets/icons/linkerd-control-plane.png +keywords: +- service-mesh +kubeVersion: '>=1.22.0-0' +maintainers: +- email: cncf-linkerd-dev@lists.cncf.io + name: Linkerd authors + url: https://linkerd.io/ +name: linkerd-control-plane +sources: +- https://github.com/linkerd/linkerd2/ +type: application +version: 2024.10.4 diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/README.md b/charts/buoyant/linkerd-control-plane/2024.10.4/README.md new file mode 100644 index 000000000..b8af0c340 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/README.md @@ -0,0 +1,321 @@ +# linkerd-control-plane + +Linkerd gives you observability, reliability, and security +for your microservices — with no code change required. + +![Version: 2024.10.4](https://img.shields.io/badge/Version-2024.10.4-informational?style=flat-square) +![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) +![AppVersion: edge-XX.X.X](https://img.shields.io/badge/AppVersion-edge--XX.X.X-informational?style=flat-square) + +**Homepage:** + +## Quickstart and documentation + +You can run Linkerd on any Kubernetes cluster in a matter of seconds. See the +[Linkerd Getting Started Guide][getting-started] for how. + +For more comprehensive documentation, start with the [Linkerd +docs][linkerd-docs]. + +## Prerequisite: linkerd-crds chart + +Before installing this chart, please install the `linkerd-crds` chart, which +creates all the CRDs that the components from the current chart require. + +## Prerequisite: identity certificates + +The identity component of Linkerd requires setting up a trust anchor +certificate, and an issuer certificate with its key. These need to be provided +to Helm by the user (unlike when using the `linkerd install` CLI which can +generate these automatically). You can provide your own, or follow [these +instructions](https://linkerd.io/2/tasks/generate-certificates/) to generate new +ones. + +Alternatively, both trust anchor and identity issuer certificates may be +derived from in-cluster resources. Existing CA (trust anchor) certificates +**must** live in a `ConfigMap` resource named `linkerd-identity-trust-roots`. +Issuer certificates **must** live in a `Secret` named +`linkerd-identity-issuer`. Both resources should exist in the control-plane's +install namespace. In order to use an existing CA, Linkerd needs to be +installed with `identity.externalCA=true`. To use an existing issuer +certificate, Linkerd should be installed with +`identity.issuer.scheme=kubernetes.io/tls`. + +A more comprehensive description is in the [automatic certificate rotation +guide](https://linkerd.io/2.12/tasks/automatically-rotating-control-plane-tls-credentials/#a-note-on-third-party-cert-management-solutions). + +Note that the provided certificates must be ECDSA certificates. + +## Adding Linkerd's Helm repository + +Included here for completeness-sake, but should have already been added when +`linkerd-base` was installed. + +```bash +# To add the repo for Linkerd edge releases: +helm repo add linkerd https://helm.linkerd.io/edge +``` + +## Installing the chart + +You must provide the certificates and keys described in the preceding section, +and the same expiration date you used to generate the Issuer certificate. + +```bash +helm install linkerd-control-plane -n linkerd \ + --set-file identityTrustAnchorsPEM=ca.crt \ + --set-file identity.issuer.tls.crtPEM=issuer.crt \ + --set-file identity.issuer.tls.keyPEM=issuer.key \ + linkerd/linkerd-control-plane +``` + +Note that you require to install this chart in the same namespace you installed +the `linkerd-base` chart. + +## Setting High-Availability + +Besides the default `values.yaml` file, the chart provides a `values-ha.yaml` +file that overrides some default values as to set things up under a +high-availability scenario, analogous to the `--ha` option in `linkerd install`. +Values such as higher number of replicas, higher memory/cpu limits and +affinities are specified in that file. + +You can get ahold of `values-ha.yaml` by fetching the chart files: + +```bash +helm fetch --untar linkerd/linkerd-control-plane +``` + +Then use the `-f` flag to provide the override file, for example: + +```bash +helm install linkerd-control-plane -n linkerd \ + --set-file identityTrustAnchorsPEM=ca.crt \ + --set-file identity.issuer.tls.crtPEM=issuer.crt \ + --set-file identity.issuer.tls.keyPEM=issuer.key \ + -f linkerd2/values-ha.yaml + linkerd/linkerd-control-plane +``` + +## Get involved + +* Check out Linkerd's source code at [GitHub][linkerd2]. +* Join Linkerd's [user mailing list][linkerd-users], [developer mailing + list][linkerd-dev], and [announcements mailing list][linkerd-announce]. +* Follow [@linkerd][twitter] on Twitter. +* Join the [Linkerd Slack][slack]. + +[getting-started]: https://linkerd.io/2/getting-started/ +[linkerd2]: https://github.com/linkerd/linkerd2 +[linkerd-announce]: https://lists.cncf.io/g/cncf-linkerd-announce +[linkerd-dev]: https://lists.cncf.io/g/cncf-linkerd-dev +[linkerd-docs]: https://linkerd.io/2/overview/ +[linkerd-users]: https://lists.cncf.io/g/cncf-linkerd-users +[slack]: http://slack.linkerd.io +[twitter]: https://twitter.com/linkerd + +## Extensions for Linkerd + +The current chart installs the core Linkerd components, which grant you +reliability and security features. Other functionality is available through +extensions. Check the corresponding docs for each one of the following +extensions: + +* Observability: + [Linkerd-viz](https://github.com/linkerd/linkerd2/blob/main/viz/charts/linkerd-viz/README.md) +* Multicluster: + [Linkerd-multicluster](https://github.com/linkerd/linkerd2/blob/main/multicluster/charts/linkerd-multicluster/README.md) +* Tracing: + [Linkerd-jaeger](https://github.com/linkerd/linkerd2/blob/main/jaeger/charts/linkerd-jaeger/README.md) + +## Requirements + +Kubernetes: `>=1.22.0-0` + +| Repository | Name | Version | +|------------|------|---------| +| file://../partials | partials | 0.1.0 | + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| clusterDomain | string | `"cluster.local"` | Kubernetes DNS Domain name to use | +| clusterNetworks | string | `"10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16,fd00::/8"` | The cluster networks for which service discovery is performed. This should include the pod and service networks, but need not include the node network. By default, all IPv4 private networks and all accepted IPv6 ULAs are specified so that resolution works in typical Kubernetes environments. | +| cniEnabled | bool | `false` | enabling this omits the NET_ADMIN capability in the PSP and the proxy-init container when injecting the proxy; requires the linkerd-cni plugin to already be installed | +| commonLabels | object | `{}` | Labels to apply to all resources | +| controlPlaneTracing | bool | `false` | enables control plane tracing | +| controlPlaneTracingNamespace | string | `"linkerd-jaeger"` | namespace to send control plane traces to | +| controller.podDisruptionBudget | object | `{"maxUnavailable":1}` | sets pod disruption budget parameter for all deployments | +| controller.podDisruptionBudget.maxUnavailable | int | `1` | Maximum number of pods that can be unavailable during disruption | +| controllerGID | int | `-1` | Optional customisation of the group ID for the control plane components (the group ID will be omitted if lower than 0) | +| controllerImage | string | `"cr.l5d.io/linkerd/controller"` | Docker image for the destination and identity components | +| controllerImageVersion | string | `""` | Optionally allow a specific container image Tag (or SHA) to be specified for the controllerImage. | +| controllerLogFormat | string | `"plain"` | Log format for the control plane components | +| controllerLogLevel | string | `"info"` | Log level for the control plane components | +| controllerReplicas | int | `1` | Number of replicas for each control plane pod | +| controllerUID | int | `2103` | User ID for the control plane components | +| debugContainer.image.name | string | `"cr.l5d.io/linkerd/debug"` | Docker image for the debug container | +| debugContainer.image.pullPolicy | string | imagePullPolicy | Pull policy for the debug container image | +| debugContainer.image.version | string | linkerdVersion | Tag for the debug container image | +| deploymentStrategy | object | `{"rollingUpdate":{"maxSurge":"25%","maxUnavailable":"25%"}}` | default kubernetes deployment strategy | +| destinationController.livenessProbe.timeoutSeconds | int | `1` | | +| destinationController.meshedHttp2ClientProtobuf.keep_alive.interval.seconds | int | `10` | | +| destinationController.meshedHttp2ClientProtobuf.keep_alive.timeout.seconds | int | `3` | | +| destinationController.meshedHttp2ClientProtobuf.keep_alive.while_idle | bool | `true` | | +| destinationController.readinessProbe.timeoutSeconds | int | `1` | | +| disableHeartBeat | bool | `false` | Set to true to not start the heartbeat cronjob | +| disableIPv6 | bool | `true` | disables routing IPv6 traffic in addition to IPv4 traffic through the proxy (IPv6 routing only available as of proxy-init v2.3.0 and linkerd-cni v1.4.0) | +| enableEndpointSlices | bool | `true` | enables the use of EndpointSlice informers for the destination service; enableEndpointSlices should be set to true only if EndpointSlice K8s feature gate is on | +| enableH2Upgrade | bool | `true` | Allow proxies to perform transparent HTTP/2 upgrading | +| enablePSP | bool | `false` | Add a PSP resource and bind it to the control plane ServiceAccounts. Note PSP has been deprecated since k8s v1.21 | +| enablePodAntiAffinity | bool | `false` | enables pod anti affinity creation on deployments for high availability | +| enablePodDisruptionBudget | bool | `false` | enables the creation of pod disruption budgets for control plane components | +| enablePprof | bool | `false` | enables the use of pprof endpoints on control plane component's admin servers | +| identity.externalCA | bool | `false` | If the linkerd-identity-trust-roots ConfigMap has already been created | +| identity.issuer.clockSkewAllowance | string | `"20s"` | Amount of time to allow for clock skew within a Linkerd cluster | +| identity.issuer.issuanceLifetime | string | `"24h0m0s"` | Amount of time for which the Identity issuer should certify identity | +| identity.issuer.scheme | string | `"linkerd.io/tls"` | | +| identity.issuer.tls | object | `{"crtPEM":"","keyPEM":""}` | Which scheme is used for the identity issuer secret format | +| identity.issuer.tls.crtPEM | string | `""` | Issuer certificate (ECDSA). It must be provided during install. | +| identity.issuer.tls.keyPEM | string | `""` | Key for the issuer certificate (ECDSA). It must be provided during install | +| identity.kubeAPI.clientBurst | int | `200` | Burst value over clientQPS | +| identity.kubeAPI.clientQPS | int | `100` | Maximum QPS sent to the kube-apiserver before throttling. See [token bucket rate limiter implementation](https://github.com/kubernetes/client-go/blob/v12.0.0/util/flowcontrol/throttle.go) | +| identity.livenessProbe.timeoutSeconds | int | `1` | | +| identity.readinessProbe.timeoutSeconds | int | `1` | | +| identity.serviceAccountTokenProjection | bool | `true` | Use [Service Account token Volume projection](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#service-account-token-volume-projection) for pod validation instead of the default token | +| identityTrustAnchorsPEM | string | `""` | Trust root certificate (ECDSA). It must be provided during install. | +| identityTrustDomain | string | clusterDomain | Trust domain used for identity | +| imagePullPolicy | string | `"IfNotPresent"` | Docker image pull policy | +| imagePullSecrets | list | `[]` | For Private docker registries, authentication is needed. Registry secrets are applied to the respective service accounts | +| kubeAPI.clientBurst | int | `200` | Burst value over clientQPS | +| kubeAPI.clientQPS | int | `100` | Maximum QPS sent to the kube-apiserver before throttling. See [token bucket rate limiter implementation](https://github.com/kubernetes/client-go/blob/v12.0.0/util/flowcontrol/throttle.go) | +| linkerdVersion | string | `"linkerdVersionValue"` | control plane version. See Proxy section for proxy version | +| networkValidator.connectAddr | string | `""` | Address to which the network-validator will attempt to connect. This should be an IP that the cluster is expected to be able to reach but a port it should not, e.g., a public IP for public clusters and a private IP for air-gapped clusters with a port like 20001. If empty, defaults to 1.1.1.1:20001 and [fd00::1]:20001 for IPv4 and IPv6 respectively. | +| networkValidator.enableSecurityContext | bool | `true` | Include a securityContext in the network-validator pod spec | +| networkValidator.listenAddr | string | `""` | Address to which network-validator listens to requests from itself. If empty, defaults to 0.0.0.0:4140 and [::]:4140 for IPv4 and IPv6 respectively. | +| networkValidator.logFormat | string | plain | Log format (`plain` or `json`) for network-validator | +| networkValidator.logLevel | string | debug | Log level for the network-validator | +| networkValidator.timeout | string | `"10s"` | Timeout before network-validator fails to validate the pod's network connectivity | +| nodeSelector | object | `{"kubernetes.io/os":"linux"}` | NodeSelector section, See the [K8S documentation](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) for more information | +| podAnnotations | object | `{}` | Additional annotations to add to all pods | +| podLabels | object | `{}` | Additional labels to add to all pods | +| podMonitor.controller.enabled | bool | `true` | Enables the creation of PodMonitor for the control-plane | +| podMonitor.controller.namespaceSelector | string | `"matchNames:\n - {{ .Release.Namespace }}\n - linkerd-viz\n - linkerd-jaeger\n"` | Selector to select which namespaces the Endpoints objects are discovered from | +| podMonitor.enabled | bool | `false` | Enables the creation of Prometheus Operator [PodMonitor](https://prometheus-operator.dev/docs/operator/api/#monitoring.coreos.com/v1.PodMonitor) | +| podMonitor.labels | object | `{}` | Labels to apply to all pod Monitors | +| podMonitor.proxy.enabled | bool | `true` | Enables the creation of PodMonitor for the data-plane | +| podMonitor.scrapeInterval | string | `"10s"` | Interval at which metrics should be scraped | +| podMonitor.scrapeTimeout | string | `"10s"` | Iimeout after which the scrape is ended | +| podMonitor.serviceMirror.enabled | bool | `true` | Enables the creation of PodMonitor for the Service Mirror component | +| policyController.image.name | string | `"cr.l5d.io/linkerd/policy-controller"` | Docker image for the policy controller | +| policyController.image.pullPolicy | string | imagePullPolicy | Pull policy for the policy controller container image | +| policyController.image.version | string | linkerdVersion | Tag for the policy controller container image | +| policyController.livenessProbe.timeoutSeconds | int | `1` | | +| policyController.logLevel | string | `"info"` | Log level for the policy controller | +| policyController.probeNetworks | list | `["0.0.0.0/0","::/0"]` | The networks from which probes are performed. By default, all networks are allowed so that all probes are authorized. | +| policyController.readinessProbe.timeoutSeconds | int | `1` | | +| policyController.resources | object | `{"cpu":{"limit":"","request":""},"ephemeral-storage":{"limit":"","request":""},"memory":{"limit":"","request":""}}` | policy controller resource requests & limits | +| policyController.resources.cpu.limit | string | `""` | Maximum amount of CPU units that the policy controller can use | +| policyController.resources.cpu.request | string | `""` | Amount of CPU units that the policy controller requests | +| policyController.resources.ephemeral-storage.limit | string | `""` | Maximum amount of ephemeral storage that the policy controller can use | +| policyController.resources.ephemeral-storage.request | string | `""` | Amount of ephemeral storage that the policy controller requests | +| policyController.resources.memory.limit | string | `""` | Maximum amount of memory that the policy controller can use | +| policyController.resources.memory.request | string | `""` | Maximum amount of memory that the policy controller requests | +| policyValidator.caBundle | string | `""` | Bundle of CA certificates for proxy injector. If not provided nor injected with cert-manager, then Helm will use the certificate generated for `policyValidator.crtPEM`. If `policyValidator.externalSecret` is set to true, this value, injectCaFrom, or injectCaFromSecret must be set, as no certificate will be generated. See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector) for more information. | +| policyValidator.crtPEM | string | `""` | Certificate for the policy validator. If not provided and not using an external secret then Helm will generate one. | +| policyValidator.externalSecret | bool | `false` | Do not create a secret resource for the policyValidator webhook. If this is set to `true`, the value `policyValidator.caBundle` must be set or the ca bundle must injected with cert-manager ca injector using `policyValidator.injectCaFrom` or `policyValidator.injectCaFromSecret` (see below). | +| policyValidator.injectCaFrom | string | `""` | Inject the CA bundle from a cert-manager Certificate. See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector/#injecting-ca-data-from-a-certificate-resource) for more information. | +| policyValidator.injectCaFromSecret | string | `""` | Inject the CA bundle from a Secret. If set, the `cert-manager.io/inject-ca-from-secret` annotation will be added to the webhook. The Secret must have the CA Bundle stored in the `ca.crt` key and have the `cert-manager.io/allow-direct-injection` annotation set to `true`. See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector/#injecting-ca-data-from-a-secret-resource) for more information. | +| policyValidator.keyPEM | string | `""` | Certificate key for the policy validator. If not provided and not using an external secret then Helm will generate one. | +| policyValidator.namespaceSelector | object | `{"matchExpressions":[{"key":"config.linkerd.io/admission-webhooks","operator":"NotIn","values":["disabled"]}]}` | Namespace selector used by admission webhook | +| priorityClassName | string | `""` | Kubernetes priorityClassName for the Linkerd Pods | +| profileValidator.caBundle | string | `""` | Bundle of CA certificates for proxy injector. If not provided nor injected with cert-manager, then Helm will use the certificate generated for `profileValidator.crtPEM`. If `profileValidator.externalSecret` is set to true, this value, injectCaFrom, or injectCaFromSecret must be set, as no certificate will be generated. See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector) for more information. | +| profileValidator.crtPEM | string | `""` | Certificate for the service profile validator. If not provided and not using an external secret then Helm will generate one. | +| profileValidator.externalSecret | bool | `false` | Do not create a secret resource for the profileValidator webhook. If this is set to `true`, the value `proxyInjector.caBundle` must be set or the ca bundle must injected with cert-manager ca injector using `proxyInjector.injectCaFrom` or `proxyInjector.injectCaFromSecret` (see below). | +| profileValidator.injectCaFrom | string | `""` | Inject the CA bundle from a cert-manager Certificate. See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector/#injecting-ca-data-from-a-certificate-resource) for more information. | +| profileValidator.injectCaFromSecret | string | `""` | Inject the CA bundle from a Secret. If set, the `cert-manager.io/inject-ca-from-secret` annotation will be added to the webhook. The Secret must have the CA Bundle stored in the `ca.crt` key and have the `cert-manager.io/allow-direct-injection` annotation set to `true`. See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector/#injecting-ca-data-from-a-secret-resource) for more information. | +| profileValidator.keyPEM | string | `""` | Certificate key for the service profile validator. If not provided and not using an external secret then Helm will generate one. | +| profileValidator.namespaceSelector | object | `{"matchExpressions":[{"key":"config.linkerd.io/admission-webhooks","operator":"NotIn","values":["disabled"]}]}` | Namespace selector used by admission webhook | +| prometheusUrl | string | `""` | url of external prometheus instance (used for the heartbeat) | +| proxy.await | bool | `true` | If set, the application container will not start until the proxy is ready | +| proxy.control.streams.idleTimeout | string | `"5m"` | The timeout between consecutive updates from the control plane. | +| proxy.control.streams.initialTimeout | string | `"3s"` | The timeout for the first update from the control plane. | +| proxy.control.streams.lifetime | string | `"1h"` | The maximum duration for a response stream (i.e. before it will be reinitialized). | +| proxy.cores | int | `0` | The `cpu.limit` and `cores` should be kept in sync. The value of `cores` must be an integer and should typically be set by rounding up from the limit. E.g. if cpu.limit is '1500m', cores should be 2. | +| proxy.defaultInboundPolicy | string | "all-unauthenticated" | The default allow policy to use when no `Server` selects a pod. One of: "all-authenticated", "all-unauthenticated", "cluster-authenticated", "cluster-unauthenticated", "deny", "audit" | +| proxy.disableInboundProtocolDetectTimeout | bool | `false` | When set to true, disables the protocol detection timeout on the inbound side of the proxy by setting it to a very high value | +| proxy.disableOutboundProtocolDetectTimeout | bool | `false` | When set to true, disables the protocol detection timeout on the outbound side of the proxy by setting it to a very high value | +| proxy.enableExternalProfiles | bool | `false` | Enable service profiles for non-Kubernetes services | +| proxy.enableShutdownEndpoint | bool | `false` | Enables the proxy's /shutdown admin endpoint | +| proxy.gid | int | `-1` | Optional customisation of the group id under which the proxy runs (the group ID will be omitted if lower than 0) | +| proxy.image.name | string | `"cr.l5d.io/linkerd/proxy"` | Docker image for the proxy | +| proxy.image.pullPolicy | string | imagePullPolicy | Pull policy for the proxy container image | +| proxy.image.version | string | linkerdVersion | Tag for the proxy container image | +| proxy.inbound.server.http2.keepAliveInterval | string | `"10s"` | The interval at which PINGs are issued to remote HTTP/2 clients. | +| proxy.inbound.server.http2.keepAliveTimeout | string | `"3s"` | The timeout within which keep-alive PINGs must be acknowledged on inbound HTTP/2 connections. | +| proxy.inboundConnectTimeout | string | `"100ms"` | Maximum time allowed for the proxy to establish an inbound TCP connection | +| proxy.inboundDiscoveryCacheUnusedTimeout | string | `"90s"` | Maximum time allowed before an unused inbound discovery result is evicted from the cache | +| proxy.livenessProbe | object | `{"initialDelaySeconds":10,"timeoutSeconds":1}` | LivenessProbe timeout and delay configuration | +| proxy.logFormat | string | `"plain"` | Log format (`plain` or `json`) for the proxy | +| proxy.logHTTPHeaders | `off` or `insecure` | `"off"` | If set to `off`, will prevent the proxy from logging HTTP headers. If set to `insecure`, HTTP headers may be logged verbatim. Note that setting this to `insecure` is not alone sufficient to log HTTP headers; the proxy logLevel must also be set to debug. | +| proxy.logLevel | string | `"warn,linkerd=info,hickory=error"` | Log level for the proxy | +| proxy.nativeSidecar | bool | `false` | Enable KEP-753 native sidecars This is an experimental feature. It requires Kubernetes >= 1.29. If enabled, .proxy.waitBeforeExitSeconds should not be used. | +| proxy.opaquePorts | string | `"25,587,3306,4444,5432,6379,9300,11211"` | Default set of opaque ports - SMTP (25,587) server-first - MYSQL (3306) server-first - Galera (4444) server-first - PostgreSQL (5432) server-first - Redis (6379) server-first - ElasticSearch (9300) server-first - Memcached (11211) clients do not issue any preamble, which breaks detection | +| proxy.outbound.server.http2.keepAliveInterval | string | `"10s"` | The interval at which PINGs are issued to local application HTTP/2 clients. | +| proxy.outbound.server.http2.keepAliveTimeout | string | `"3s"` | The timeout within which keep-alive PINGs must be acknowledged on outbound HTTP/2 connections. | +| proxy.outboundConnectTimeout | string | `"1000ms"` | Maximum time allowed for the proxy to establish an outbound TCP connection | +| proxy.outboundDiscoveryCacheUnusedTimeout | string | `"5s"` | Maximum time allowed before an unused outbound discovery result is evicted from the cache | +| proxy.ports.admin | int | `4191` | Admin port for the proxy container | +| proxy.ports.control | int | `4190` | Control port for the proxy container | +| proxy.ports.inbound | int | `4143` | Inbound port for the proxy container | +| proxy.ports.outbound | int | `4140` | Outbound port for the proxy container | +| proxy.readinessProbe | object | `{"initialDelaySeconds":2,"timeoutSeconds":1}` | ReadinessProbe timeout and delay configuration | +| proxy.requireIdentityOnInboundPorts | string | `""` | | +| proxy.resources.cpu.limit | string | `""` | Maximum amount of CPU units that the proxy can use | +| proxy.resources.cpu.request | string | `""` | Amount of CPU units that the proxy requests | +| proxy.resources.ephemeral-storage.limit | string | `""` | Maximum amount of ephemeral storage that the proxy can use | +| proxy.resources.ephemeral-storage.request | string | `""` | Amount of ephemeral storage that the proxy requests | +| proxy.resources.memory.limit | string | `""` | Maximum amount of memory that the proxy can use | +| proxy.resources.memory.request | string | `""` | Maximum amount of memory that the proxy requests | +| proxy.shutdownGracePeriod | string | `""` | Grace period for graceful proxy shutdowns. If this timeout elapses before all open connections have completed, the proxy will terminate forcefully, closing any remaining connections. | +| proxy.startupProbe.failureThreshold | int | `120` | | +| proxy.startupProbe.initialDelaySeconds | int | `0` | | +| proxy.startupProbe.periodSeconds | int | `1` | | +| proxy.uid | int | `2102` | User id under which the proxy runs | +| proxy.waitBeforeExitSeconds | int | `0` | If set the injected proxy sidecars in the data plane will stay alive for at least the given period before receiving the SIGTERM signal from Kubernetes but no longer than the pod's `terminationGracePeriodSeconds`. See [Lifecycle hooks](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks) for more info on container lifecycle hooks. | +| proxyInit.closeWaitTimeoutSecs | int | `0` | | +| proxyInit.ignoreInboundPorts | string | `"4567,4568"` | Default set of inbound ports to skip via iptables - Galera (4567,4568) | +| proxyInit.ignoreOutboundPorts | string | `"4567,4568"` | Default set of outbound ports to skip via iptables - Galera (4567,4568) | +| proxyInit.image.name | string | `"cr.l5d.io/linkerd/proxy-init"` | Docker image for the proxy-init container | +| proxyInit.image.pullPolicy | string | imagePullPolicy | Pull policy for the proxy-init container image | +| proxyInit.image.version | string | `"v2.4.1"` | Tag for the proxy-init container image | +| proxyInit.iptablesMode | string | `"legacy"` | Variant of iptables that will be used to configure routing. Currently, proxy-init can be run either in 'nft' or in 'legacy' mode. The mode will control which utility binary will be called. The host must support whichever mode will be used | +| proxyInit.kubeAPIServerPorts | string | `"443,6443"` | Default set of ports to skip via iptables for control plane components so they can communicate with the Kubernetes API Server | +| proxyInit.logFormat | string | plain | Log format (`plain` or `json`) for the proxy-init | +| proxyInit.logLevel | string | info | Log level for the proxy-init | +| proxyInit.privileged | bool | false | Privileged mode allows the container processes to inherit all security capabilities and bypass any security limitations enforced by the kubelet. When used with 'runAsRoot: true', the container will behave exactly as if it was running as root on the host. May escape cgroup limits and see other processes and devices on the host. | +| proxyInit.runAsGroup | int | `65534` | This value is used only if runAsRoot is false; otherwise runAsGroup will be 0 | +| proxyInit.runAsRoot | bool | `false` | Allow overriding the runAsNonRoot behaviour () | +| proxyInit.runAsUser | int | `65534` | This value is used only if runAsRoot is false; otherwise runAsUser will be 0 | +| proxyInit.skipSubnets | string | `""` | Comma-separated list of subnets in valid CIDR format that should be skipped by the proxy | +| proxyInit.xtMountPath.mountPath | string | `"/run"` | | +| proxyInit.xtMountPath.name | string | `"linkerd-proxy-init-xtables-lock"` | | +| proxyInjector.caBundle | string | `""` | Bundle of CA certificates for proxy injector. If not provided nor injected with cert-manager, then Helm will use the certificate generated for `proxyInjector.crtPEM`. If `proxyInjector.externalSecret` is set to true, this value, injectCaFrom, or injectCaFromSecret must be set, as no certificate will be generated. See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector) for more information. | +| proxyInjector.crtPEM | string | `""` | Certificate for the proxy injector. If not provided and not using an external secret then Helm will generate one. | +| proxyInjector.externalSecret | bool | `false` | Do not create a secret resource for the proxyInjector webhook. If this is set to `true`, the value `proxyInjector.caBundle` must be set or the ca bundle must injected with cert-manager ca injector using `proxyInjector.injectCaFrom` or `proxyInjector.injectCaFromSecret` (see below). | +| proxyInjector.injectCaFrom | string | `""` | Inject the CA bundle from a cert-manager Certificate. See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector/#injecting-ca-data-from-a-certificate-resource) for more information. | +| proxyInjector.injectCaFromSecret | string | `""` | Inject the CA bundle from a Secret. If set, the `cert-manager.io/inject-ca-from-secret` annotation will be added to the webhook. The Secret must have the CA Bundle stored in the `ca.crt` key and have the `cert-manager.io/allow-direct-injection` annotation set to `true`. See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector/#injecting-ca-data-from-a-secret-resource) for more information. | +| proxyInjector.keyPEM | string | `""` | Certificate key for the proxy injector. If not provided and not using an external secret then Helm will generate one. | +| proxyInjector.livenessProbe.timeoutSeconds | int | `1` | | +| proxyInjector.namespaceSelector | object | `{"matchExpressions":[{"key":"config.linkerd.io/admission-webhooks","operator":"NotIn","values":["disabled"]},{"key":"kubernetes.io/metadata.name","operator":"NotIn","values":["kube-system","cert-manager"]}]}` | Namespace selector used by admission webhook. | +| proxyInjector.objectSelector | object | `{"matchExpressions":[{"key":"linkerd.io/control-plane-component","operator":"DoesNotExist"},{"key":"linkerd.io/cni-resource","operator":"DoesNotExist"}]}` | Object selector used by admission webhook. | +| proxyInjector.readinessProbe.timeoutSeconds | int | `1` | | +| proxyInjector.timeoutSeconds | int | `10` | Timeout in seconds before the API Server cancels a request to the proxy injector. If timeout is exceeded, the webhookfailurePolicy is used. | +| revisionHistoryLimit | int | `10` | Specifies the number of old ReplicaSets to retain to allow rollback. | +| runtimeClassName | string | `""` | Runtime Class Name for all the pods | +| spValidator | object | `{"livenessProbe":{"timeoutSeconds":1},"readinessProbe":{"timeoutSeconds":1}}` | SP validator configuration | +| webhookFailurePolicy | string | `"Ignore"` | Failure policy for the proxy injector | + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.12.0](https://github.com/norwoodj/helm-docs/releases/v1.12.0) diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/README.md.gotmpl b/charts/buoyant/linkerd-control-plane/2024.10.4/README.md.gotmpl new file mode 100644 index 000000000..19da2a82d --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/README.md.gotmpl @@ -0,0 +1,133 @@ +{{ template "chart.header" . }} +{{ template "chart.description" . }} + +{{ template "chart.versionBadge" . }} +{{ template "chart.typeBadge" . }} +{{ template "chart.appVersionBadge" . }} + +{{ template "chart.homepageLine" . }} + +## Quickstart and documentation + +You can run Linkerd on any Kubernetes cluster in a matter of seconds. See the +[Linkerd Getting Started Guide][getting-started] for how. + +For more comprehensive documentation, start with the [Linkerd +docs][linkerd-docs]. + +## Prerequisite: linkerd-crds chart + +Before installing this chart, please install the `linkerd-crds` chart, which +creates all the CRDs that the components from the current chart require. + +## Prerequisite: identity certificates + +The identity component of Linkerd requires setting up a trust anchor +certificate, and an issuer certificate with its key. These need to be provided +to Helm by the user (unlike when using the `linkerd install` CLI which can +generate these automatically). You can provide your own, or follow [these +instructions](https://linkerd.io/2/tasks/generate-certificates/) to generate new +ones. + +Alternatively, both trust anchor and identity issuer certificates may be +derived from in-cluster resources. Existing CA (trust anchor) certificates +**must** live in a `ConfigMap` resource named `linkerd-identity-trust-roots`. +Issuer certificates **must** live in a `Secret` named +`linkerd-identity-issuer`. Both resources should exist in the control-plane's +install namespace. In order to use an existing CA, Linkerd needs to be +installed with `identity.externalCA=true`. To use an existing issuer +certificate, Linkerd should be installed with +`identity.issuer.scheme=kubernetes.io/tls`. + +A more comprehensive description is in the [automatic certificate rotation +guide](https://linkerd.io/2.12/tasks/automatically-rotating-control-plane-tls-credentials/#a-note-on-third-party-cert-management-solutions). + +Note that the provided certificates must be ECDSA certificates. + +## Adding Linkerd's Helm repository + +Included here for completeness-sake, but should have already been added when +`linkerd-base` was installed. + +```bash +# To add the repo for Linkerd edge releases: +helm repo add linkerd https://helm.linkerd.io/edge +``` + +## Installing the chart + +You must provide the certificates and keys described in the preceding section, +and the same expiration date you used to generate the Issuer certificate. + +```bash +helm install linkerd-control-plane -n linkerd \ + --set-file identityTrustAnchorsPEM=ca.crt \ + --set-file identity.issuer.tls.crtPEM=issuer.crt \ + --set-file identity.issuer.tls.keyPEM=issuer.key \ + linkerd/linkerd-control-plane +``` + +Note that you require to install this chart in the same namespace you installed +the `linkerd-base` chart. + +## Setting High-Availability + +Besides the default `values.yaml` file, the chart provides a `values-ha.yaml` +file that overrides some default values as to set things up under a +high-availability scenario, analogous to the `--ha` option in `linkerd install`. +Values such as higher number of replicas, higher memory/cpu limits and +affinities are specified in that file. + +You can get ahold of `values-ha.yaml` by fetching the chart files: + +```bash +helm fetch --untar linkerd/linkerd-control-plane +``` + +Then use the `-f` flag to provide the override file, for example: + +```bash +helm install linkerd-control-plane -n linkerd \ + --set-file identityTrustAnchorsPEM=ca.crt \ + --set-file identity.issuer.tls.crtPEM=issuer.crt \ + --set-file identity.issuer.tls.keyPEM=issuer.key \ + -f linkerd2/values-ha.yaml + linkerd/linkerd-control-plane +``` + +## Get involved + +* Check out Linkerd's source code at [GitHub][linkerd2]. +* Join Linkerd's [user mailing list][linkerd-users], [developer mailing + list][linkerd-dev], and [announcements mailing list][linkerd-announce]. +* Follow [@linkerd][twitter] on Twitter. +* Join the [Linkerd Slack][slack]. + +[getting-started]: https://linkerd.io/2/getting-started/ +[linkerd2]: https://github.com/linkerd/linkerd2 +[linkerd-announce]: https://lists.cncf.io/g/cncf-linkerd-announce +[linkerd-dev]: https://lists.cncf.io/g/cncf-linkerd-dev +[linkerd-docs]: https://linkerd.io/2/overview/ +[linkerd-users]: https://lists.cncf.io/g/cncf-linkerd-users +[slack]: http://slack.linkerd.io +[twitter]: https://twitter.com/linkerd + +## Extensions for Linkerd + +The current chart installs the core Linkerd components, which grant you +reliability and security features. Other functionality is available through +extensions. Check the corresponding docs for each one of the following +extensions: + +* Observability: + [Linkerd-viz](https://github.com/linkerd/linkerd2/blob/main/viz/charts/linkerd-viz/README.md) +* Multicluster: + [Linkerd-multicluster](https://github.com/linkerd/linkerd2/blob/main/multicluster/charts/linkerd-multicluster/README.md) +* Tracing: + [Linkerd-jaeger](https://github.com/linkerd/linkerd2/blob/main/jaeger/charts/linkerd-jaeger/README.md) + +{{ template "chart.requirementsSection" . }} + +{{ template "chart.valuesSection" . }} + +{{ template "helm-docs.versionFooter" . }} diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/app-readme.md b/charts/buoyant/linkerd-control-plane/2024.10.4/app-readme.md new file mode 100644 index 000000000..351eac5f0 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/app-readme.md @@ -0,0 +1,14 @@ +# Linkerd 2 Chart + +Linkerd is an ultra light, ultra simple, ultra powerful service mesh. Linkerd +adds security, observability, and reliability to Kubernetes, without the +complexity. + +This particular Helm chart only installs the control plane core. You will also need to install the +linkerd-crds chart. This chart should be automatically installed along with any other dependencies. +If it is not installed as a dependency, install it first. + +To gain access to the observability features, please install the linkerd-viz chart. +Other extensions are available (multicluster, jaeger) under the linkerd Helm repo. + +Full documentation available at: https://linkerd.io/2/overview/ diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/.helmignore b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/.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/buoyant/linkerd-control-plane/2024.10.4/charts/partials/Chart.yaml b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/Chart.yaml new file mode 100644 index 000000000..23cfc167e --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +description: 'A Helm chart containing Linkerd partial templates, depended by the ''linkerd'' + and ''patch'' charts. ' +name: partials +version: 0.1.0 diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/README.md b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/README.md new file mode 100644 index 000000000..10805c9b9 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/README.md @@ -0,0 +1,9 @@ +# partials + +A Helm chart containing Linkerd partial templates, +depended by the 'linkerd' and 'patch' charts. + +![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.12.0](https://github.com/norwoodj/helm-docs/releases/v1.12.0) diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/README.md.gotmpl b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/README.md.gotmpl new file mode 100644 index 000000000..37f510106 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/README.md.gotmpl @@ -0,0 +1,14 @@ +{{ template "chart.header" . }} +{{ template "chart.description" . }} + +{{ template "chart.versionBadge" . }} +{{ template "chart.typeBadge" . }} +{{ template "chart.appVersionBadge" . }} + +{{ template "chart.homepageLine" . }} + +{{ template "chart.requirementsSection" . }} + +{{ template "chart.valuesSection" . }} + +{{ template "helm-docs.versionFooter" . }} diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/NOTES.txt b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/NOTES.txt new file mode 100644 index 000000000..e69de29bb diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_affinity.tpl b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_affinity.tpl new file mode 100644 index 000000000..5dde1da47 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_affinity.tpl @@ -0,0 +1,38 @@ +{{ define "linkerd.pod-affinity" -}} +podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchExpressions: + - key: {{ default "linkerd.io/control-plane-component" .label }} + operator: In + values: + - {{ .component }} + topologyKey: topology.kubernetes.io/zone + weight: 100 + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: {{ default "linkerd.io/control-plane-component" .label }} + operator: In + values: + - {{ .component }} + topologyKey: kubernetes.io/hostname +{{- end }} + +{{ define "linkerd.node-affinity" -}} +nodeAffinity: +{{- toYaml .Values.nodeAffinity | trim | nindent 2 }} +{{- end }} + +{{ define "linkerd.affinity" -}} +{{- if or .Values.enablePodAntiAffinity .Values.nodeAffinity -}} +affinity: +{{- end }} +{{- if .Values.enablePodAntiAffinity -}} +{{- include "linkerd.pod-affinity" . | nindent 2 }} +{{- end }} +{{- if .Values.nodeAffinity -}} +{{- include "linkerd.node-affinity" . | nindent 2 }} +{{- end }} +{{- end }} diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_capabilities.tpl b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_capabilities.tpl new file mode 100644 index 000000000..a595d74c1 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_capabilities.tpl @@ -0,0 +1,16 @@ +{{- define "partials.proxy.capabilities" -}} +capabilities: + {{- if .Values.proxy.capabilities.add }} + add: + {{- toYaml .Values.proxy.capabilities.add | trim | nindent 4 }} + {{- end }} + {{- if .Values.proxy.capabilities.drop }} + drop: + {{- toYaml .Values.proxy.capabilities.drop | trim | nindent 4 }} + {{- end }} +{{- end -}} + +{{- define "partials.proxy-init.capabilities.drop" -}} +drop: +{{ toYaml .Values.proxyInit.capabilities.drop | trim }} +{{- end -}} diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_debug.tpl b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_debug.tpl new file mode 100644 index 000000000..4df8cc77b --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_debug.tpl @@ -0,0 +1,15 @@ +{{- define "partials.debug" -}} +image: {{.Values.debugContainer.image.name}}:{{.Values.debugContainer.image.version | default .Values.linkerdVersion}} +imagePullPolicy: {{.Values.debugContainer.image.pullPolicy | default .Values.imagePullPolicy}} +name: linkerd-debug +terminationMessagePolicy: FallbackToLogsOnError +# some environments require probes, so we provide some infallible ones +livenessProbe: + exec: + command: + - "true" +readinessProbe: + exec: + command: + - "true" +{{- end -}} diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_helpers.tpl b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_helpers.tpl new file mode 100644 index 000000000..b6cdc34d0 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_helpers.tpl @@ -0,0 +1,14 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Splits a coma separated list into a list of string values. +For example "11,22,55,44" will become "11","22","55","44" +*/}} +{{- define "partials.splitStringList" -}} +{{- if gt (len (toString .)) 0 -}} +{{- $ports := toString . | splitList "," -}} +{{- $last := sub (len $ports) 1 -}} +{{- range $i,$port := $ports -}} +"{{$port}}"{{ternary "," "" (ne $i $last)}} +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_metadata.tpl b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_metadata.tpl new file mode 100644 index 000000000..04d2f1bea --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_metadata.tpl @@ -0,0 +1,17 @@ +{{- define "partials.annotations.created-by" -}} +linkerd.io/created-by: {{ .Values.cliVersion | default (printf "linkerd/helm %s" ( (.Values.image).version | default .Values.linkerdVersion)) }} +{{- end -}} + +{{- define "partials.proxy.annotations" -}} +linkerd.io/proxy-version: {{.Values.proxy.image.version | default .Values.linkerdVersion}} +cluster-autoscaler.kubernetes.io/safe-to-evict: "true" +linkerd.io/trust-root-sha256: {{ .Values.identityTrustAnchorsPEM | sha256sum }} +{{- end -}} + +{{/* +To add labels to the control-plane components, instead update at individual component manifests as +adding here would also update `spec.selector.matchLabels` which are immutable and would fail upgrades. +*/}} +{{- define "partials.proxy.labels" -}} +linkerd.io/proxy-{{.workloadKind}}: {{.component}} +{{- end -}} diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_network-validator.tpl b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_network-validator.tpl new file mode 100644 index 000000000..276056395 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_network-validator.tpl @@ -0,0 +1,45 @@ +{{- define "partials.network-validator" -}} +name: linkerd-network-validator +image: {{.Values.proxy.image.name}}:{{.Values.proxy.image.version | default .Values.linkerdVersion }} +imagePullPolicy: {{.Values.proxy.image.pullPolicy | default .Values.imagePullPolicy}} +{{ include "partials.resources" .Values.proxy.resources }} +{{- if or .Values.networkValidator.enableSecurityContext }} +securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsGroup: 65534 + runAsNonRoot: true + runAsUser: 65534 + seccompProfile: + type: RuntimeDefault +{{- end }} +command: + - /usr/lib/linkerd/linkerd2-network-validator +args: + - --log-format + - {{ .Values.networkValidator.logFormat }} + - --log-level + - {{ .Values.networkValidator.logLevel }} + - --connect-addr + {{- if .Values.networkValidator.connectAddr }} + - {{ .Values.networkValidator.connectAddr | quote }} + {{- else if .Values.disableIPv6}} + - "1.1.1.1:20001" + {{- else }} + - "[fd00::1]:20001" + {{- end }} + - --listen-addr + {{- if .Values.networkValidator.listenAddr }} + - {{ .Values.networkValidator.listenAddr | quote }} + {{- else if .Values.disableIPv6}} + - "0.0.0.0:4140" + {{- else }} + - "[::]:4140" + {{- end }} + - --timeout + - {{ .Values.networkValidator.timeout }} + +{{- end -}} diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_nodeselector.tpl b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_nodeselector.tpl new file mode 100644 index 000000000..4cde0ab16 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_nodeselector.tpl @@ -0,0 +1,4 @@ +{{- define "linkerd.node-selector" -}} +nodeSelector: +{{- toYaml .Values.nodeSelector | trim | nindent 2 }} +{{- end -}} diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_proxy-config-ann.tpl b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_proxy-config-ann.tpl new file mode 100644 index 000000000..9651b3bd1 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_proxy-config-ann.tpl @@ -0,0 +1,18 @@ +{{- define "partials.proxy.config.annotations" -}} +{{- with .cpu }} +{{- with .request -}} +config.linkerd.io/proxy-cpu-request: {{. | quote}} +{{end}} +{{- with .limit -}} +config.linkerd.io/proxy-cpu-limit: {{. | quote}} +{{- end}} +{{- end}} +{{- with .memory }} +{{- with .request }} +config.linkerd.io/proxy-memory-request: {{. | quote}} +{{end}} +{{- with .limit -}} +config.linkerd.io/proxy-memory-limit: {{. | quote}} +{{- end}} +{{- end }} +{{- end }} diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_proxy-init.tpl b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_proxy-init.tpl new file mode 100644 index 000000000..a307b1407 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_proxy-init.tpl @@ -0,0 +1,98 @@ +{{- define "partials.proxy-init" -}} +args: +{{- if (.Values.proxyInit.iptablesMode | default "legacy" | eq "nft") }} +- --firewall-bin-path +- "iptables-nft" +- --firewall-save-bin-path +- "iptables-nft-save" +{{- else if not (eq .Values.proxyInit.iptablesMode "legacy") }} +{{ fail (printf "Unsupported value \"%s\" for proxyInit.iptablesMode\nValid values: [\"nft\", \"legacy\"]" .Values.proxyInit.iptablesMode) }} +{{end -}} +{{- if .Values.disableIPv6 }} +- --ipv6=false +{{- end }} +- --incoming-proxy-port +- {{.Values.proxy.ports.inbound | quote}} +- --outgoing-proxy-port +- {{.Values.proxy.ports.outbound | quote}} +- --proxy-uid +- {{.Values.proxy.uid | quote}} +{{- if ge (int .Values.proxy.gid) 0 }} +- --proxy-gid +- {{.Values.proxy.gid | quote}} +{{- end }} +- --inbound-ports-to-ignore +- "{{.Values.proxy.ports.control}},{{.Values.proxy.ports.admin}}{{ternary (printf ",%s" (.Values.proxyInit.ignoreInboundPorts | toString)) "" (not (empty .Values.proxyInit.ignoreInboundPorts)) }}" +{{- if .Values.proxyInit.ignoreOutboundPorts }} +- --outbound-ports-to-ignore +- {{.Values.proxyInit.ignoreOutboundPorts | quote}} +{{- end }} +{{- if .Values.proxyInit.closeWaitTimeoutSecs }} +- --timeout-close-wait-secs +- {{ .Values.proxyInit.closeWaitTimeoutSecs | quote}} +{{- end }} +{{- if .Values.proxyInit.logFormat }} +- --log-format +- {{ .Values.proxyInit.logFormat }} +{{- end }} +{{- if .Values.proxyInit.logLevel }} +- --log-level +- {{ .Values.proxyInit.logLevel }} +{{- end }} +{{- if .Values.proxyInit.skipSubnets }} +- --subnets-to-ignore +- {{ .Values.proxyInit.skipSubnets | quote }} +{{- end }} +image: {{.Values.proxyInit.image.name}}:{{.Values.proxyInit.image.version}} +imagePullPolicy: {{.Values.proxyInit.image.pullPolicy | default .Values.imagePullPolicy}} +name: linkerd-init +{{ include "partials.resources" .Values.proxy.resources }} +securityContext: + {{- if or .Values.proxyInit.closeWaitTimeoutSecs .Values.proxyInit.privileged }} + allowPrivilegeEscalation: true + {{- else }} + allowPrivilegeEscalation: false + {{- end }} + capabilities: + add: + - NET_ADMIN + - NET_RAW + {{- if .Values.proxyInit.capabilities -}} + {{- if .Values.proxyInit.capabilities.add }} + {{- toYaml .Values.proxyInit.capabilities.add | trim | nindent 4 }} + {{- end }} + {{- if .Values.proxyInit.capabilities.drop -}} + {{- include "partials.proxy-init.capabilities.drop" . | nindent 4 -}} + {{- end }} + {{- end }} + {{- if or .Values.proxyInit.closeWaitTimeoutSecs .Values.proxyInit.privileged }} + privileged: true + {{- else }} + privileged: false + {{- end }} + {{- if .Values.proxyInit.runAsRoot }} + runAsGroup: 0 + runAsNonRoot: false + runAsUser: 0 + {{- else }} + runAsNonRoot: true + runAsUser: {{ .Values.proxyInit.runAsUser | int | eq 0 | ternary 65534 .Values.proxyInit.runAsUser }} + runAsGroup: {{ .Values.proxyInit.runAsGroup | int | eq 0 | ternary 65534 .Values.proxyInit.runAsGroup }} + {{- end }} + readOnlyRootFilesystem: true + seccompProfile: + type: RuntimeDefault +terminationMessagePolicy: FallbackToLogsOnError +{{- if or (not .Values.cniEnabled) .Values.proxyInit.saMountPath }} +volumeMounts: +{{- end -}} +{{- if not .Values.cniEnabled }} +- mountPath: {{.Values.proxyInit.xtMountPath.mountPath}} + name: {{.Values.proxyInit.xtMountPath.name}} +{{- end -}} +{{- if .Values.proxyInit.saMountPath }} +- mountPath: {{.Values.proxyInit.saMountPath.mountPath}} + name: {{.Values.proxyInit.saMountPath.name}} + readOnly: {{.Values.proxyInit.saMountPath.readOnly}} +{{- end -}} +{{- end -}} diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_proxy.tpl b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_proxy.tpl new file mode 100644 index 000000000..4dcf12dee --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_proxy.tpl @@ -0,0 +1,271 @@ +{{ define "partials.proxy" -}} +{{ if and .Values.proxy.nativeSidecar .Values.proxy.waitBeforeExitSeconds }} +{{ fail "proxy.nativeSidecar and waitBeforeExitSeconds cannot be used simultaneously" }} +{{- end }} +{{- if not (has .Values.proxy.logHTTPHeaders (list "insecure" "off" "")) }} +{{- fail "logHTTPHeaders must be one of: insecure | off" }} +{{- end }} +{{- $trustDomain := (.Values.identityTrustDomain | default .Values.clusterDomain) -}} +env: +- name: _pod_name + valueFrom: + fieldRef: + fieldPath: metadata.name +- name: _pod_ns + valueFrom: + fieldRef: + fieldPath: metadata.namespace +- name: _pod_nodeName + valueFrom: + fieldRef: + fieldPath: spec.nodeName +{{- if .Values.proxy.cores }} +- name: LINKERD2_PROXY_CORES + value: {{.Values.proxy.cores | quote}} +{{- end }} +{{ if .Values.proxy.requireIdentityOnInboundPorts -}} +- name: LINKERD2_PROXY_INBOUND_PORTS_REQUIRE_IDENTITY + value: {{.Values.proxy.requireIdentityOnInboundPorts | quote}} +{{ end -}} +{{ if .Values.proxy.requireTLSOnInboundPorts -}} +- name: LINKERD2_PROXY_INBOUND_PORTS_REQUIRE_TLS + value: {{.Values.proxy.requireTLSOnInboundPorts | quote}} +{{ end -}} +- name: LINKERD2_PROXY_SHUTDOWN_ENDPOINT_ENABLED + value: {{.Values.proxy.enableShutdownEndpoint | quote}} +- name: LINKERD2_PROXY_LOG + value: "{{.Values.proxy.logLevel}}{{ if not (eq .Values.proxy.logHTTPHeaders "insecure") }},[{headers}]=off,[{request}]=off{{ end }}" +- name: LINKERD2_PROXY_LOG_FORMAT + value: {{.Values.proxy.logFormat | quote}} +- name: LINKERD2_PROXY_DESTINATION_SVC_ADDR + value: {{ternary "localhost.:8086" (printf "linkerd-dst-headless.%s.svc.%s.:8086" .Release.Namespace .Values.clusterDomain) (eq (toString .Values.proxy.component) "linkerd-destination")}} +- name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS + value: {{.Values.clusterNetworks | quote}} +- name: LINKERD2_PROXY_POLICY_SVC_ADDR + value: {{ternary "localhost.:8090" (printf "linkerd-policy.%s.svc.%s.:8090" .Release.Namespace .Values.clusterDomain) (eq (toString .Values.proxy.component) "linkerd-destination")}} +- name: LINKERD2_PROXY_POLICY_WORKLOAD + value: | + {"ns":"$(_pod_ns)", "pod":"$(_pod_name)"} +- name: LINKERD2_PROXY_INBOUND_DEFAULT_POLICY + value: {{.Values.proxy.defaultInboundPolicy}} +- name: LINKERD2_PROXY_POLICY_CLUSTER_NETWORKS + value: {{.Values.clusterNetworks | quote}} +- name: LINKERD2_PROXY_CONTROL_STREAM_INITIAL_TIMEOUT + value: {{((.Values.proxy.control).streams).initialTimeout | default "" | quote}} +- name: LINKERD2_PROXY_CONTROL_STREAM_IDLE_TIMEOUT + value: {{((.Values.proxy.control).streams).idleTimeout | default "" | quote}} +- name: LINKERD2_PROXY_CONTROL_STREAM_LIFETIME + value: {{((.Values.proxy.control).streams).lifetime | default "" | quote}} +{{ if .Values.proxy.inboundConnectTimeout -}} +- name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT + value: {{.Values.proxy.inboundConnectTimeout | quote}} +{{ end -}} +{{ if .Values.proxy.outboundConnectTimeout -}} +- name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT + value: {{.Values.proxy.outboundConnectTimeout | quote}} +{{ end -}} +{{ if .Values.proxy.outboundDiscoveryCacheUnusedTimeout -}} +- name: LINKERD2_PROXY_OUTBOUND_DISCOVERY_IDLE_TIMEOUT + value: {{.Values.proxy.outboundDiscoveryCacheUnusedTimeout | quote}} +{{ end -}} +{{ if .Values.proxy.inboundDiscoveryCacheUnusedTimeout -}} +- name: LINKERD2_PROXY_INBOUND_DISCOVERY_IDLE_TIMEOUT + value: {{.Values.proxy.inboundDiscoveryCacheUnusedTimeout | quote}} +{{ end -}} +{{ if .Values.proxy.disableOutboundProtocolDetectTimeout -}} +- name: LINKERD2_PROXY_OUTBOUND_DETECT_TIMEOUT + value: "365d" +{{ end -}} +{{ if .Values.proxy.disableInboundProtocolDetectTimeout -}} +- name: LINKERD2_PROXY_INBOUND_DETECT_TIMEOUT + value: "365d" +{{ end -}} +- name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR + value: "{{ if .Values.disableIPv6 }}0.0.0.0{{ else }}[::]{{ end }}:{{.Values.proxy.ports.control}}" +- name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR + value: "{{ if .Values.disableIPv6 }}0.0.0.0{{ else }}[::]{{ end }}:{{.Values.proxy.ports.admin}}" +{{- /* Deprecated, superseded by LINKERD2_PROXY_OUTBOUND_LISTEN_ADDRS since proxy's v2.228.0 (deployed since edge-24.4.5) */}} +- name: LINKERD2_PROXY_OUTBOUND_LISTEN_ADDR + value: "127.0.0.1:{{.Values.proxy.ports.outbound}}" +- name: LINKERD2_PROXY_OUTBOUND_LISTEN_ADDRS + value: "127.0.0.1:{{.Values.proxy.ports.outbound}}{{ if not .Values.disableIPv6}},[::1]:{{.Values.proxy.ports.outbound}}{{ end }}" +- name: LINKERD2_PROXY_INBOUND_LISTEN_ADDR + value: "{{ if .Values.disableIPv6 }}0.0.0.0{{ else }}[::]{{ end }}:{{.Values.proxy.ports.inbound}}" +- name: LINKERD2_PROXY_INBOUND_IPS + valueFrom: + fieldRef: + fieldPath: status.podIPs +- name: LINKERD2_PROXY_INBOUND_PORTS + value: {{ .Values.proxy.podInboundPorts | quote }} +{{ if .Values.proxy.isGateway -}} +- name: LINKERD2_PROXY_INBOUND_GATEWAY_SUFFIXES + value: {{printf "svc.%s." .Values.clusterDomain}} +{{ end -}} +{{ if .Values.proxy.isIngress -}} +- name: LINKERD2_PROXY_INGRESS_MODE + value: "true" +{{ end -}} +- name: LINKERD2_PROXY_DESTINATION_PROFILE_SUFFIXES + {{- $internalDomain := printf "svc.%s." .Values.clusterDomain }} + value: {{ternary "." $internalDomain .Values.proxy.enableExternalProfiles}} +- name: LINKERD2_PROXY_INBOUND_ACCEPT_KEEPALIVE + value: 10000ms +- name: LINKERD2_PROXY_OUTBOUND_CONNECT_KEEPALIVE + value: 10000ms +- name: LINKERD2_PROXY_INBOUND_ACCEPT_USER_TIMEOUT + value: 30s +- name: LINKERD2_PROXY_OUTBOUND_CONNECT_USER_TIMEOUT + value: 30s +{{- /* Configure inbound and outbound parameters, e.g. for HTTP/2 servers. */}} +{{ range $proxyK, $proxyV := (dict "inbound" .Values.proxy.inbound "outbound" .Values.proxy.outbound) -}} +{{ range $scopeK, $scopeV := $proxyV -}} +{{ range $protoK, $protoV := $scopeV -}} +{{ range $paramK, $paramV := $protoV -}} +- name: LINKERD2_PROXY_{{snakecase $proxyK | upper}}_{{snakecase $scopeK | upper}}_{{snakecase $protoK | upper}}_{{snakecase $paramK | upper}} + value: {{ quote $paramV }} +{{ end -}} +{{ end -}} +{{ end -}} +{{ end -}} +{{ if .Values.proxy.opaquePorts -}} +- name: LINKERD2_PROXY_INBOUND_PORTS_DISABLE_PROTOCOL_DETECTION + value: {{.Values.proxy.opaquePorts | quote}} +{{ end -}} +- name: LINKERD2_PROXY_DESTINATION_CONTEXT + value: | + {"ns":"$(_pod_ns)", "nodeName":"$(_pod_nodeName)", "pod":"$(_pod_name)"} +- name: _pod_sa + valueFrom: + fieldRef: + fieldPath: spec.serviceAccountName +- name: _l5d_ns + value: {{.Release.Namespace}} +- name: _l5d_trustdomain + value: {{$trustDomain}} +- name: LINKERD2_PROXY_IDENTITY_DIR + value: /var/run/linkerd/identity/end-entity +- name: LINKERD2_PROXY_IDENTITY_TRUST_ANCHORS +{{- /* +Pods in the `linkerd` namespace are not injected by the proxy injector and instead obtain +the trust anchor bundle from the `linkerd-identity-trust-roots` configmap. This should not +be used in other contexts. +*/}} +{{- if .Values.proxy.loadTrustBundleFromConfigMap }} + valueFrom: + configMapKeyRef: + name: linkerd-identity-trust-roots + key: ca-bundle.crt +{{ else }} + value: | + {{- required "Please provide the identity trust anchors" .Values.identityTrustAnchorsPEM | trim | nindent 4 }} +{{ end -}} +- name: LINKERD2_PROXY_IDENTITY_TOKEN_FILE +{{- if .Values.identity.serviceAccountTokenProjection }} + value: /var/run/secrets/tokens/linkerd-identity-token +{{ else }} + value: /var/run/secrets/kubernetes.io/serviceaccount/token +{{ end -}} +- name: LINKERD2_PROXY_IDENTITY_SVC_ADDR + value: {{ternary "localhost.:8080" (printf "linkerd-identity-headless.%s.svc.%s.:8080" .Release.Namespace .Values.clusterDomain) (eq (toString .Values.proxy.component) "linkerd-identity")}} +- name: LINKERD2_PROXY_IDENTITY_LOCAL_NAME + value: $(_pod_sa).$(_pod_ns).serviceaccount.identity.{{.Release.Namespace}}.{{$trustDomain}} +- name: LINKERD2_PROXY_IDENTITY_SVC_NAME + value: linkerd-identity.{{.Release.Namespace}}.serviceaccount.identity.{{.Release.Namespace}}.{{$trustDomain}} +- name: LINKERD2_PROXY_DESTINATION_SVC_NAME + value: linkerd-destination.{{.Release.Namespace}}.serviceaccount.identity.{{.Release.Namespace}}.{{$trustDomain}} +- name: LINKERD2_PROXY_POLICY_SVC_NAME + value: linkerd-destination.{{.Release.Namespace}}.serviceaccount.identity.{{.Release.Namespace}}.{{$trustDomain}} +{{ if .Values.proxy.accessLog -}} +- name: LINKERD2_PROXY_ACCESS_LOG + value: {{.Values.proxy.accessLog | quote}} +{{ end -}} +{{ if .Values.proxy.shutdownGracePeriod -}} +- name: LINKERD2_PROXY_SHUTDOWN_GRACE_PERIOD + value: {{.Values.proxy.shutdownGracePeriod | quote}} +{{ end -}} +{{ if .Values.proxy.additionalEnv -}} +{{ toYaml .Values.proxy.additionalEnv }} +{{ end -}} +{{ if .Values.proxy.experimentalEnv -}} +{{ toYaml .Values.proxy.experimentalEnv }} +{{ end -}} +image: {{.Values.proxy.image.name}}:{{.Values.proxy.image.version | default .Values.linkerdVersion}} +imagePullPolicy: {{.Values.proxy.image.pullPolicy | default .Values.imagePullPolicy}} +livenessProbe: + httpGet: + path: /live + port: {{.Values.proxy.ports.admin}} + initialDelaySeconds: {{.Values.proxy.livenessProbe.initialDelaySeconds }} + timeoutSeconds: {{.Values.proxy.livenessProbe.timeoutSeconds }} +name: linkerd-proxy +ports: +- containerPort: {{.Values.proxy.ports.inbound}} + name: linkerd-proxy +- containerPort: {{.Values.proxy.ports.admin}} + name: linkerd-admin +readinessProbe: + httpGet: + path: /ready + port: {{.Values.proxy.ports.admin}} + initialDelaySeconds: {{.Values.proxy.readinessProbe.initialDelaySeconds }} + timeoutSeconds: {{.Values.proxy.readinessProbe.timeoutSeconds }} +{{- if and .Values.proxy.nativeSidecar .Values.proxy.await }} +startupProbe: + httpGet: + path: /ready + port: {{.Values.proxy.ports.admin}} + initialDelaySeconds: {{.Values.proxy.startupProbe.initialDelaySeconds}} + periodSeconds: {{.Values.proxy.startupProbe.periodSeconds}} + failureThreshold: {{.Values.proxy.startupProbe.failureThreshold}} +{{- end }} +{{- if .Values.proxy.resources }} +{{ include "partials.resources" .Values.proxy.resources }} +{{- end }} +securityContext: + allowPrivilegeEscalation: false + {{- if .Values.proxy.capabilities -}} + {{- include "partials.proxy.capabilities" . | nindent 2 -}} + {{- end }} + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: {{.Values.proxy.uid}} +{{- if ge (int .Values.proxy.gid) 0 }} + runAsGroup: {{.Values.proxy.gid}} +{{- end }} + seccompProfile: + type: RuntimeDefault +terminationMessagePolicy: FallbackToLogsOnError +{{- if and (not .Values.proxy.nativeSidecar) (or .Values.proxy.await .Values.proxy.waitBeforeExitSeconds) }} +lifecycle: +{{- if .Values.proxy.await }} + postStart: + exec: + command: + - /usr/lib/linkerd/linkerd-await + - --timeout=2m + - --port={{.Values.proxy.ports.admin}} +{{- end }} +{{- if .Values.proxy.waitBeforeExitSeconds }} + preStop: + exec: + command: + - /bin/sleep + - {{.Values.proxy.waitBeforeExitSeconds | quote}} +{{- end }} +{{- end }} +volumeMounts: +- mountPath: /var/run/linkerd/identity/end-entity + name: linkerd-identity-end-entity +{{- if .Values.identity.serviceAccountTokenProjection }} +- mountPath: /var/run/secrets/tokens + name: linkerd-identity-token +{{- end }} +{{- if .Values.proxy.saMountPath }} +- mountPath: {{.Values.proxy.saMountPath.mountPath}} + name: {{.Values.proxy.saMountPath.name}} + readOnly: {{.Values.proxy.saMountPath.readOnly}} +{{- end -}} +{{- if .Values.proxy.nativeSidecar }} +restartPolicy: Always +{{- end -}} +{{- end }} diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_pull-secrets.tpl b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_pull-secrets.tpl new file mode 100644 index 000000000..0c9aa4f01 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_pull-secrets.tpl @@ -0,0 +1,6 @@ +{{- define "partials.image-pull-secrets"}} +{{- if . }} +imagePullSecrets: +{{ toYaml . | indent 2 }} +{{- end }} +{{- end -}} diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_resources.tpl b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_resources.tpl new file mode 100644 index 000000000..1fd6789fd --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_resources.tpl @@ -0,0 +1,28 @@ +{{- define "partials.resources" -}} +{{- $ephemeralStorage := index . "ephemeral-storage" -}} +resources: + {{- if or (.cpu).limit (.memory).limit ($ephemeralStorage).limit }} + limits: + {{- with (.cpu).limit }} + cpu: {{. | quote}} + {{- end }} + {{- with (.memory).limit }} + memory: {{. | quote}} + {{- end }} + {{- with ($ephemeralStorage).limit }} + ephemeral-storage: {{. | quote}} + {{- end }} + {{- end }} + {{- if or (.cpu).request (.memory).request ($ephemeralStorage).request }} + requests: + {{- with (.cpu).request }} + cpu: {{. | quote}} + {{- end }} + {{- with (.memory).request }} + memory: {{. | quote}} + {{- end }} + {{- with ($ephemeralStorage).request }} + ephemeral-storage: {{. | quote}} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_tolerations.tpl b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_tolerations.tpl new file mode 100644 index 000000000..c2292b146 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_tolerations.tpl @@ -0,0 +1,4 @@ +{{- define "linkerd.tolerations" -}} +tolerations: +{{ toYaml .Values.tolerations | trim | indent 2 }} +{{- end -}} diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_trace.tpl b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_trace.tpl new file mode 100644 index 000000000..dee059541 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_trace.tpl @@ -0,0 +1,5 @@ +{{ define "partials.linkerd.trace" -}} +{{ if .Values.controlPlaneTracing -}} +- -trace-collector=collector.{{.Values.controlPlaneTracingNamespace}}.svc.{{.Values.clusterDomain}}:55678 +{{ end -}} +{{- end }} diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_validate.tpl b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_validate.tpl new file mode 100644 index 000000000..ba772c2fe --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_validate.tpl @@ -0,0 +1,19 @@ +{{- define "linkerd.webhook.validation" -}} + +{{- if and (.injectCaFrom) (.injectCaFromSecret) -}} +{{- fail "injectCaFrom and injectCaFromSecret cannot both be set" -}} +{{- end -}} + +{{- if and (or (.injectCaFrom) (.injectCaFromSecret)) (.caBundle) -}} +{{- fail "injectCaFrom or injectCaFromSecret cannot be set if providing a caBundle" -}} +{{- end -}} + +{{- if and (.externalSecret) (empty .caBundle) (empty .injectCaFrom) (empty .injectCaFromSecret) -}} +{{- fail "if externalSecret is set, then caBundle, injectCaFrom, or injectCaFromSecret must be set" -}} +{{- end }} + +{{- if and (or .injectCaFrom .injectCaFromSecret .caBundle) (not .externalSecret) -}} +{{- fail "if caBundle, injectCaFrom, or injectCaFromSecret is set, then externalSecret must be set" -}} +{{- end -}} + +{{- end -}} diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_volumes.tpl b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_volumes.tpl new file mode 100644 index 000000000..ecb24cfe6 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/templates/_volumes.tpl @@ -0,0 +1,41 @@ +{{ define "partials.proxy.volumes.identity" -}} +emptyDir: + medium: Memory +name: linkerd-identity-end-entity +{{- end -}} + +{{ define "partials.proxyInit.volumes.xtables" -}} +emptyDir: {} +name: {{ .Values.proxyInit.xtMountPath.name }} +{{- end -}} + +{{- define "partials.proxy.volumes.service-account-token" -}} +name: linkerd-identity-token +projected: + sources: + - serviceAccountToken: + path: linkerd-identity-token + expirationSeconds: 86400 {{- /* # 24 hours */}} + audience: identity.l5d.io +{{- end -}} + +{{- define "partials.volumes.manual-mount-service-account-token" -}} +name: kube-api-access +projected: + defaultMode: 420 + sources: + - serviceAccountToken: + expirationSeconds: 3607 + path: token + - configMap: + items: + - key: ca.crt + path: ca.crt + name: kube-root-ca.crt + - downwardAPI: + items: + - fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + path: namespace +{{- end -}} \ No newline at end of file diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/values.yaml b/charts/buoyant/linkerd-control-plane/2024.10.4/charts/partials/values.yaml new file mode 100644 index 000000000..e69de29bb diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/questions.yaml b/charts/buoyant/linkerd-control-plane/2024.10.4/questions.yaml new file mode 100644 index 000000000..4ae27870a --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/questions.yaml @@ -0,0 +1,19 @@ +questions: +- variable: identityTrustAnchorsPEM + label: "Trust root certificate (ECDSA)" + description: "Root certificate used to support mTLS connections between meshed pods" + required: true + type: multiline + group: Identity +- variable: identity.issuer.tls.crtPEM + label: "Issuer certificate (ECDSA)" + description: "Intermediate certificate, rooted on identityTrustAnchorsPEM, used to sign the Linkerd proxies' CSR" + required: true + type: multiline + group: Identity +- variable: identity.issuer.tls.keyPEM + label: "Key for the issuer certificate (ECDSA)" + description: "Private key for the certificate entered on crtPEM" + required: true + type: multiline + group: Identity diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/templates/NOTES.txt b/charts/buoyant/linkerd-control-plane/2024.10.4/templates/NOTES.txt new file mode 100644 index 000000000..4bd1be9fc --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/templates/NOTES.txt @@ -0,0 +1,19 @@ +The Linkerd control plane was successfully installed 🎉 + +To help you manage your Linkerd service mesh you can install the Linkerd CLI by running: + + curl -sL https://run.linkerd.io/install | sh + +Alternatively, you can download the CLI directly via the Linkerd releases page: + + https://github.com/linkerd/linkerd2/releases/ + +To make sure everything works as expected, run the following: + + linkerd check + +The viz extension can be installed by running: + + helm install linkerd-viz linkerd/linkerd-viz + +Looking for more? Visit https://linkerd.io/2/getting-started/ diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/templates/config-rbac.yaml b/charts/buoyant/linkerd-control-plane/2024.10.4/templates/config-rbac.yaml new file mode 100644 index 000000000..5f5c34203 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/templates/config-rbac.yaml @@ -0,0 +1,16 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} + name: ext-namespace-metadata-linkerd-config + namespace: {{ .Release.Namespace }} +rules: +- apiGroups: [""] + resources: ["configmaps"] + verbs: ["get"] + resourceNames: ["linkerd-config"] diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/templates/config.yaml b/charts/buoyant/linkerd-control-plane/2024.10.4/templates/config.yaml new file mode 100644 index 000000000..a9cea5f42 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/templates/config.yaml @@ -0,0 +1,39 @@ +--- +kind: ConfigMap +apiVersion: v1 +metadata: + name: linkerd-config + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: controller + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +data: + linkerd-crds-chart-version: linkerd-crds-1.0.0-edge + values: | + {{- $values := deepCopy .Values }} + {{- /* + WARNING! All sensitive or private data such as TLS keys must be removed + here to avoid it being publicly readable. + */ -}} + {{- if kindIs "map" $values.identity.issuer.tls -}} + {{- $_ := unset $values.identity.issuer.tls "keyPEM"}} + {{- end -}} + {{- if kindIs "map" $values.profileValidator -}} + {{- $_ := unset $values.profileValidator "keyPEM"}} + {{- end -}} + {{- if kindIs "map" $values.proxyInjector -}} + {{- $_ := unset $values.proxyInjector "keyPEM"}} + {{- end -}} + {{- if kindIs "map" $values.policyValidator -}} + {{- $_ := unset $values.policyValidator "keyPEM"}} + {{- end -}} + {{- if (empty $values.identityTrustDomain) -}} + {{- $_ := set $values "identityTrustDomain" $values.clusterDomain}} + {{- end -}} + {{- $_ := unset $values "partials"}} + {{- $_ := unset $values "configs"}} + {{- $_ := unset $values "stage"}} + {{- toYaml $values | trim | nindent 4 }} diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/templates/destination-rbac.yaml b/charts/buoyant/linkerd-control-plane/2024.10.4/templates/destination-rbac.yaml new file mode 100644 index 000000000..6ab21b3a7 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/templates/destination-rbac.yaml @@ -0,0 +1,336 @@ +--- +### +### Destination Controller Service +### +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: linkerd-{{.Release.Namespace}}-destination + labels: + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +rules: +- apiGroups: ["apps"] + resources: ["replicasets"] + verbs: ["list", "get", "watch"] +- apiGroups: ["batch"] + resources: ["jobs"] + verbs: ["list", "get", "watch"] +- apiGroups: [""] + resources: ["pods", "endpoints", "services", "nodes"] + verbs: ["list", "get", "watch"] +- apiGroups: ["linkerd.io"] + resources: ["serviceprofiles"] + verbs: ["list", "get", "watch"] +- apiGroups: ["workload.linkerd.io"] + resources: ["externalworkloads"] + verbs: ["list", "get", "watch"] +- apiGroups: ["coordination.k8s.io"] + resources: ["leases"] + verbs: ["create", "get", "update", "patch"] + {{- if .Values.enableEndpointSlices }} +- apiGroups: ["discovery.k8s.io"] + resources: ["endpointslices"] + verbs: ["list", "get", "watch", "create", "update", "patch", "delete"] + {{- end }} +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: linkerd-{{.Release.Namespace}}-destination + labels: + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: linkerd-{{.Release.Namespace}}-destination +subjects: +- kind: ServiceAccount + name: linkerd-destination + namespace: {{.Release.Namespace}} +--- +kind: ServiceAccount +apiVersion: v1 +metadata: + name: linkerd-destination + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +{{- include "partials.image-pull-secrets" .Values.imagePullSecrets }} +--- +{{- $host := printf "linkerd-sp-validator.%s.svc" .Release.Namespace }} +{{- $ca := genSelfSignedCert $host (list) (list $host) 365 }} +{{- if (not .Values.profileValidator.externalSecret) }} +kind: Secret +apiVersion: v1 +metadata: + name: linkerd-sp-validator-k8s-tls + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +type: kubernetes.io/tls +data: + tls.crt: {{ ternary (b64enc (trim $ca.Cert)) (b64enc (trim .Values.profileValidator.crtPEM)) (empty .Values.profileValidator.crtPEM) }} + tls.key: {{ ternary (b64enc (trim $ca.Key)) (b64enc (trim .Values.profileValidator.keyPEM)) (empty .Values.profileValidator.keyPEM) }} +--- +{{- end }} +{{- include "linkerd.webhook.validation" .Values.profileValidator }} +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: linkerd-sp-validator-webhook-config + {{- if or (.Values.profileValidator.injectCaFrom) (.Values.profileValidator.injectCaFromSecret) }} + annotations: + {{- if .Values.profileValidator.injectCaFrom }} + cert-manager.io/inject-ca-from: {{ .Values.profileValidator.injectCaFrom }} + {{- end }} + {{- if .Values.profileValidator.injectCaFromSecret }} + cert-manager.io/inject-ca-from-secret: {{ .Values.profileValidator.injectCaFromSecret }} + {{- end }} + {{- end }} + labels: + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +webhooks: +- name: linkerd-sp-validator.linkerd.io + namespaceSelector: + {{- toYaml .Values.profileValidator.namespaceSelector | trim | nindent 4 }} + clientConfig: + service: + name: linkerd-sp-validator + namespace: {{ .Release.Namespace }} + path: "/" + {{- if and (empty .Values.profileValidator.injectCaFrom) (empty .Values.profileValidator.injectCaFromSecret) }} + caBundle: {{ ternary (b64enc (trim $ca.Cert)) (b64enc (trim .Values.profileValidator.caBundle)) (empty .Values.profileValidator.caBundle) }} + {{- end }} + failurePolicy: {{.Values.webhookFailurePolicy}} + admissionReviewVersions: ["v1", "v1beta1"] + rules: + - operations: ["CREATE", "UPDATE"] + apiGroups: ["linkerd.io"] + apiVersions: ["v1alpha1", "v1alpha2"] + resources: ["serviceprofiles"] + sideEffects: None +--- +{{- $host := printf "linkerd-policy-validator.%s.svc" .Release.Namespace }} +{{- $ca := genSelfSignedCert $host (list) (list $host) 365 }} +{{- if (not .Values.policyValidator.externalSecret) }} +kind: Secret +apiVersion: v1 +metadata: + name: linkerd-policy-validator-k8s-tls + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +type: kubernetes.io/tls +data: + tls.crt: {{ ternary (b64enc (trim $ca.Cert)) (b64enc (trim .Values.policyValidator.crtPEM)) (empty .Values.policyValidator.crtPEM) }} + tls.key: {{ ternary (b64enc (trim $ca.Key)) (b64enc (trim .Values.policyValidator.keyPEM)) (empty .Values.policyValidator.keyPEM) }} +--- +{{- end }} +{{- include "linkerd.webhook.validation" .Values.policyValidator }} +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: linkerd-policy-validator-webhook-config + {{- if or (.Values.policyValidator.injectCaFrom) (.Values.policyValidator.injectCaFromSecret) }} + annotations: + {{- if .Values.policyValidator.injectCaFrom }} + cert-manager.io/inject-ca-from: {{ .Values.policyValidator.injectCaFrom }} + {{- end }} + {{- if .Values.policyValidator.injectCaFromSecret }} + cert-manager.io/inject-ca-from-secret: {{ .Values.policyValidator.injectCaFromSecret }} + {{- end }} + {{- end }} + labels: + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +webhooks: +- name: linkerd-policy-validator.linkerd.io + namespaceSelector: + {{- toYaml .Values.policyValidator.namespaceSelector | trim | nindent 4 }} + clientConfig: + service: + name: linkerd-policy-validator + namespace: {{ .Release.Namespace }} + path: "/" + {{- if and (empty .Values.policyValidator.injectCaFrom) (empty .Values.policyValidator.injectCaFromSecret) }} + caBundle: {{ ternary (b64enc (trim $ca.Cert)) (b64enc (trim .Values.policyValidator.caBundle)) (empty .Values.policyValidator.caBundle) }} + {{- end }} + failurePolicy: {{.Values.webhookFailurePolicy}} + admissionReviewVersions: ["v1", "v1beta1"] + rules: + - operations: ["CREATE", "UPDATE"] + apiGroups: ["policy.linkerd.io"] + apiVersions: ["*"] + resources: + - authorizationpolicies + - httproutes + - networkauthentications + - meshtlsauthentications + - serverauthorizations + - servers + - egressnetworks + - operations: ["CREATE", "UPDATE"] + apiGroups: ["gateway.networking.k8s.io"] + apiVersions: ["*"] + resources: + - httproutes + - grpcroutes + - tlsroutes + - tcproutes + sideEffects: None +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: linkerd-policy + labels: + app.kubernetes.io/part-of: Linkerd + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch + - apiGroups: + - apps + resources: + - deployments + verbs: + - get + - apiGroups: + - policy.linkerd.io + resources: + - authorizationpolicies + - httproutes + - meshtlsauthentications + - networkauthentications + - servers + - serverauthorizations + - egressnetworks + verbs: + - get + - list + - watch + - apiGroups: + - gateway.networking.k8s.io + resources: + - httproutes + - grpcroutes + - tlsroutes + - tcproutes + verbs: + - get + - list + - watch + - apiGroups: + - policy.linkerd.io + resources: + - httproutes/status + - egressnetworks/status + verbs: + - patch + - apiGroups: + - gateway.networking.k8s.io + resources: + - httproutes/status + - grpcroutes/status + - tlsroutes/status + - tcproutes/status + verbs: + - patch + - apiGroups: + - workload.linkerd.io + resources: + - externalworkloads + verbs: + - get + - list + - watch + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - get + - patch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: linkerd-destination-policy + labels: + app.kubernetes.io/part-of: Linkerd + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: linkerd-policy +subjects: + - kind: ServiceAccount + name: linkerd-destination + namespace: {{.Release.Namespace}} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: remote-discovery + namespace: {{.Release.Namespace}} + labels: + app.kubernetes.io/part-of: Linkerd + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +rules: + - apiGroups: + - "" + resources: + - secrets + verbs: + - get + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: linkerd-destination-remote-discovery + namespace: {{.Release.Namespace}} + labels: + app.kubernetes.io/part-of: Linkerd + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: remote-discovery +subjects: + - kind: ServiceAccount + name: linkerd-destination + namespace: {{.Release.Namespace}} diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/templates/destination.yaml b/charts/buoyant/linkerd-control-plane/2024.10.4/templates/destination.yaml new file mode 100644 index 000000000..f6eb4d444 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/templates/destination.yaml @@ -0,0 +1,447 @@ +--- +### +### Destination Controller Service +### +kind: Service +apiVersion: v1 +metadata: + name: linkerd-dst + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +spec: + type: ClusterIP + selector: + linkerd.io/control-plane-component: destination + ports: + - name: grpc + port: 8086 + targetPort: 8086 +--- +kind: Service +apiVersion: v1 +metadata: + name: linkerd-dst-headless + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +spec: + clusterIP: None + selector: + linkerd.io/control-plane-component: destination + ports: + - name: grpc + port: 8086 + targetPort: 8086 +--- +kind: Service +apiVersion: v1 +metadata: + name: linkerd-sp-validator + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +spec: + type: ClusterIP + selector: + linkerd.io/control-plane-component: destination + ports: + - name: sp-validator + port: 443 + targetPort: sp-validator +--- +kind: Service +apiVersion: v1 +metadata: + name: linkerd-policy + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +spec: + clusterIP: None + selector: + linkerd.io/control-plane-component: destination + ports: + - name: grpc + port: 8090 + targetPort: 8090 +--- +kind: Service +apiVersion: v1 +metadata: + name: linkerd-policy-validator + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +spec: + type: ClusterIP + selector: + linkerd.io/control-plane-component: destination + ports: + - name: policy-https + port: 443 + targetPort: policy-https +{{- if .Values.enablePodDisruptionBudget }} +--- +kind: PodDisruptionBudget +apiVersion: policy/v1 +metadata: + name: linkerd-dst + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +spec: + maxUnavailable: {{ .Values.controller.podDisruptionBudget.maxUnavailable }} + selector: + matchLabels: + linkerd.io/control-plane-component: destination +{{- end }} +--- +{{- $tree := deepCopy . }} +{{ $_ := set $tree.Values.proxy "workloadKind" "deployment" -}} +{{ $_ := set $tree.Values.proxy "component" "linkerd-destination" -}} +{{ $_ := set $tree.Values.proxy "waitBeforeExitSeconds" 0 -}} +{{- if not (empty .Values.destinationProxyResources) }} +{{- $c := dig "cores" .Values.proxy.cores .Values.destinationProxyResources }} +{{- $_ := set $tree.Values.proxy "cores" $c }} +{{- $r := merge .Values.destinationProxyResources .Values.proxy.resources }} +{{- $_ := set $tree.Values.proxy "resources" $r }} +{{- end }} +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + {{ include "partials.annotations.created-by" . }} + labels: + app.kubernetes.io/name: destination + app.kubernetes.io/part-of: Linkerd + app.kubernetes.io/version: {{.Values.linkerdVersion}} + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + name: linkerd-destination + namespace: {{ .Release.Namespace }} +spec: + replicas: {{.Values.controllerReplicas}} + revisionHistoryLimit: {{.Values.revisionHistoryLimit}} + selector: + matchLabels: + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- include "partials.proxy.labels" $tree.Values.proxy | nindent 6}} + {{- if .Values.deploymentStrategy }} + strategy: + {{- with .Values.deploymentStrategy }}{{ toYaml . | trim | nindent 4 }}{{- end }} + {{- end }} + template: + metadata: + annotations: + checksum/config: {{ include (print $.Template.BasePath "/destination-rbac.yaml") . | sha256sum }} + {{ include "partials.annotations.created-by" . }} + {{- include "partials.proxy.annotations" . | nindent 8}} + {{- with .Values.podAnnotations }}{{ toYaml . | trim | nindent 8 }}{{- end }} + config.linkerd.io/default-inbound-policy: "all-unauthenticated" + labels: + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + linkerd.io/workload-ns: {{.Release.Namespace}} + {{- include "partials.proxy.labels" $tree.Values.proxy | nindent 8}} + {{- with .Values.podLabels }}{{ toYaml . | trim | nindent 8 }}{{- end }} + spec: + {{- with .Values.runtimeClassName }} + runtimeClassName: {{ . | quote }} + {{- end }} + {{- if .Values.tolerations -}} + {{- include "linkerd.tolerations" . | nindent 6 }} + {{- end -}} + {{- include "linkerd.node-selector" . | nindent 6 }} + {{- $_ := set $tree "component" "destination" -}} + {{- include "linkerd.affinity" $tree | nindent 6 }} + automountServiceAccountToken: false + containers: + {{- $_ := set $tree.Values.proxy "await" $tree.Values.proxy.await }} + {{- $_ := set $tree.Values.proxy "loadTrustBundleFromConfigMap" true }} + {{- $_ := set $tree.Values.proxy "podInboundPorts" "8086,8090,8443,9443,9990,9996,9997" }} + {{- $_ := set $tree.Values.proxy "outboundDiscoveryCacheUnusedTimeout" "5s" }} + {{- $_ := set $tree.Values.proxy "inboundDiscoveryCacheUnusedTimeout" "90s" }} + {{- /* + The pod needs to accept webhook traffic, and we can't rely on that originating in the + cluster network. + */}} + {{- $_ := set $tree.Values.proxy "defaultInboundPolicy" "all-unauthenticated" }} + {{- $_ := set $tree.Values.proxy "capabilities" (dict "drop" (list "ALL")) }} + {{- if not $tree.Values.proxy.nativeSidecar }} + - {{- include "partials.proxy" $tree | indent 8 | trimPrefix (repeat 7 " ") }} + {{- end }} + - args: + - destination + - -addr=:8086 + - -controller-namespace={{.Release.Namespace}} + - -enable-h2-upgrade={{.Values.enableH2Upgrade}} + - -log-level={{.Values.controllerLogLevel}} + - -log-format={{.Values.controllerLogFormat}} + - -enable-endpoint-slices={{.Values.enableEndpointSlices}} + - -cluster-domain={{.Values.clusterDomain}} + - -identity-trust-domain={{.Values.identityTrustDomain | default .Values.clusterDomain}} + - -default-opaque-ports={{.Values.proxy.opaquePorts}} + - -enable-ipv6={{not .Values.disableIPv6}} + - -enable-pprof={{.Values.enablePprof | default false}} + {{- if (.Values.destinationController).meshedHttp2ClientProtobuf }} + - --meshed-http2-client-params={{ toJson .Values.destinationController.meshedHttp2ClientProtobuf }} + {{- end }} + {{- range (.Values.destinationController).additionalArgs }} + - {{ . }} + {{- end }} + {{- range (.Values.destinationController).experimentalArgs }} + - {{ . }} + {{- end }} + {{- if or (.Values.destinationController).additionalEnv (.Values.destinationController).experimentalEnv }} + env: + {{- with (.Values.destinationController).additionalEnv }} + {{- toYaml . | nindent 8 -}} + {{- end }} + {{- with (.Values.destinationController).experimentalEnv }} + {{- toYaml . | nindent 8 -}} + {{- end }} + {{- end }} + {{- include "partials.linkerd.trace" . | nindent 8 -}} + image: {{.Values.controllerImage}}:{{.Values.controllerImageVersion | default .Values.linkerdVersion}} + imagePullPolicy: {{.Values.imagePullPolicy}} + livenessProbe: + httpGet: + path: /ping + port: 9996 + initialDelaySeconds: 10 + {{- with (.Values.destinationController.livenessProbe).timeoutSeconds }} + timeoutSeconds: {{ . }} + {{- end }} + name: destination + ports: + - containerPort: 8086 + name: grpc + - containerPort: 9996 + name: admin-http + readinessProbe: + failureThreshold: 7 + httpGet: + path: /ready + port: 9996 + {{- with (.Values.destinationController.readinessProbe).timeoutSeconds }} + timeoutSeconds: {{ . }} + {{- end }} + {{- if .Values.destinationResources -}} + {{- include "partials.resources" .Values.destinationResources | nindent 8 }} + {{- end }} + securityContext: + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: {{.Values.controllerUID}} + {{- if ge (int .Values.controllerGID) 0 }} + runAsGroup: {{.Values.controllerGID}} + {{- end }} + allowPrivilegeEscalation: false + seccompProfile: + type: RuntimeDefault + volumeMounts: + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + name: kube-api-access + readOnly: true + - args: + - sp-validator + - -log-level={{.Values.controllerLogLevel}} + - -log-format={{.Values.controllerLogFormat}} + - -enable-pprof={{.Values.enablePprof | default false}} + {{- if or (.Values.spValidator).additionalEnv (.Values.spValidator).experimentalEnv }} + env: + {{- with (.Values.spValidator).additionalEnv }} + {{- toYaml . | nindent 8 -}} + {{- end }} + {{- with (.Values.spValidator).experimentalEnv }} + {{- toYaml . | nindent 8 -}} + {{- end }} + {{- end }} + image: {{.Values.controllerImage}}:{{.Values.controllerImageVersion | default .Values.linkerdVersion}} + imagePullPolicy: {{.Values.imagePullPolicy}} + livenessProbe: + httpGet: + path: /ping + port: 9997 + initialDelaySeconds: 10 + {{- with ((.Values.spValidator).livenessProbe).timeoutSeconds }} + timeoutSeconds: {{ . }} + {{- end }} + name: sp-validator + ports: + - containerPort: 8443 + name: sp-validator + - containerPort: 9997 + name: admin-http + readinessProbe: + failureThreshold: 7 + httpGet: + path: /ready + port: 9997 + {{- with ((.Values.spValidator).readinessProbe).timeoutSeconds }} + timeoutSeconds: {{ . }} + {{- end }} + {{- if .Values.spValidatorResources -}} + {{- include "partials.resources" .Values.spValidatorResources | nindent 8 }} + {{- end }} + securityContext: + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: {{.Values.controllerUID}} + {{- if ge (int .Values.controllerGID) 0 }} + runAsGroup: {{.Values.controllerGID}} + {{- end }} + allowPrivilegeEscalation: false + seccompProfile: + type: RuntimeDefault + volumeMounts: + - mountPath: /var/run/linkerd/tls + name: sp-tls + readOnly: true + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + name: kube-api-access + readOnly: true + - args: + - --admin-addr={{ if .Values.disableIPv6 }}0.0.0.0{{ else }}[::]{{ end }}:9990 + - --control-plane-namespace={{.Release.Namespace}} + - --grpc-addr={{ if .Values.disableIPv6 }}0.0.0.0{{ else }}[::]{{ end }}:8090 + - --server-addr={{ if .Values.disableIPv6 }}0.0.0.0{{ else }}[::]{{ end }}:9443 + - --server-tls-key=/var/run/linkerd/tls/tls.key + - --server-tls-certs=/var/run/linkerd/tls/tls.crt + - --cluster-networks={{.Values.clusterNetworks}} + - --identity-domain={{.Values.identityTrustDomain | default .Values.clusterDomain}} + - --cluster-domain={{.Values.clusterDomain}} + - --default-policy={{.Values.proxy.defaultInboundPolicy}} + - --log-level={{.Values.policyController.logLevel | default "linkerd=info,warn"}} + - --log-format={{.Values.controllerLogFormat}} + - --default-opaque-ports={{.Values.proxy.opaquePorts}} + {{- if .Values.policyController.probeNetworks }} + - --probe-networks={{.Values.policyController.probeNetworks | join ","}} + {{- end}} + {{- range .Values.policyController.additionalArgs }} + - {{ . }} + {{- end }} + {{- range .Values.policyController.experimentalArgs }} + - {{ . }} + {{- end }} + image: {{.Values.policyController.image.name}}:{{.Values.policyController.image.version | default .Values.linkerdVersion}} + imagePullPolicy: {{.Values.policyController.image.pullPolicy | default .Values.imagePullPolicy}} + livenessProbe: + httpGet: + path: /live + port: admin-http + {{- with (.Values.policyController.livenessProbe).timeoutSeconds }} + timeoutSeconds: {{ . }} + {{- end }} + name: policy + ports: + - containerPort: 8090 + name: grpc + - containerPort: 9990 + name: admin-http + - containerPort: 9443 + name: policy-https + readinessProbe: + failureThreshold: 7 + httpGet: + path: /ready + port: admin-http + initialDelaySeconds: 10 + {{- with (.Values.policyController.readinessProbe).timeoutSeconds }} + timeoutSeconds: {{ . }} + {{- end }} + {{- if .Values.policyController.resources }} + {{- include "partials.resources" .Values.policyController.resources | nindent 8 }} + {{- end }} + securityContext: + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: {{.Values.controllerUID}} + {{- if ge (int .Values.controllerGID) 0 }} + runAsGroup: {{.Values.controllerGID}} + {{- end }} + allowPrivilegeEscalation: false + seccompProfile: + type: RuntimeDefault + volumeMounts: + - mountPath: /var/run/linkerd/tls + name: policy-tls + readOnly: true + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + name: kube-api-access + readOnly: true + initContainers: + {{ if .Values.cniEnabled -}} + - {{- include "partials.network-validator" $tree | indent 8 | trimPrefix (repeat 7 " ") }} + {{ else -}} + {{- /* + The destination controller needs to connect to the Kubernetes API before the proxy is able + to proxy requests, so we always skip these connections. + */}} + {{- $_ := set $tree.Values.proxyInit "ignoreOutboundPorts" .Values.proxyInit.kubeAPIServerPorts -}} + - {{- include "partials.proxy-init" $tree | indent 8 | trimPrefix (repeat 7 " ") }} + {{ end -}} + {{- if $tree.Values.proxy.nativeSidecar }} + {{- $_ := set $tree.Values.proxy "startupProbeInitialDelaySeconds" 35 }} + {{- $_ := set $tree.Values.proxy "startupProbePeriodSeconds" 5 }} + {{- $_ := set $tree.Values.proxy "startupProbeFailureThreshold" 20 }} + - {{- include "partials.proxy" $tree | indent 8 | trimPrefix (repeat 7 " ") }} + {{ end -}} + {{- if .Values.priorityClassName -}} + priorityClassName: {{ .Values.priorityClassName }} + {{ end -}} + securityContext: + seccompProfile: + type: RuntimeDefault + serviceAccountName: linkerd-destination + volumes: + - name: sp-tls + secret: + secretName: linkerd-sp-validator-k8s-tls + - name: policy-tls + secret: + secretName: linkerd-policy-validator-k8s-tls + - {{- include "partials.volumes.manual-mount-service-account-token" . | indent 8 | trimPrefix (repeat 7 " ") }} + {{ if not .Values.cniEnabled -}} + - {{- include "partials.proxyInit.volumes.xtables" . | indent 8 | trimPrefix (repeat 7 " ") }} + {{ end -}} + {{if .Values.identity.serviceAccountTokenProjection -}} + - {{- include "partials.proxy.volumes.service-account-token" . | indent 8 | trimPrefix (repeat 7 " ") }} + {{ end -}} + - {{- include "partials.proxy.volumes.identity" . | indent 8 | trimPrefix (repeat 7 " ") }} diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/templates/heartbeat-rbac.yaml b/charts/buoyant/linkerd-control-plane/2024.10.4/templates/heartbeat-rbac.yaml new file mode 100644 index 000000000..7b127543f --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/templates/heartbeat-rbac.yaml @@ -0,0 +1,78 @@ +{{ if not .Values.disableHeartBeat -}} +--- +### +### Heartbeat RBAC +### +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: linkerd-heartbeat + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +rules: +- apiGroups: [""] + resources: ["configmaps"] + verbs: ["get"] + resourceNames: ["linkerd-config"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: linkerd-heartbeat + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +roleRef: + kind: Role + name: linkerd-heartbeat + apiGroup: rbac.authorization.k8s.io +subjects: +- kind: ServiceAccount + name: linkerd-heartbeat + namespace: {{.Release.Namespace}} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: linkerd-heartbeat + labels: + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +rules: +- apiGroups: [""] + resources: ["namespaces"] + verbs: ["list"] +- apiGroups: ["linkerd.io"] + resources: ["serviceprofiles"] + verbs: ["list"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: linkerd-heartbeat + labels: + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +roleRef: + kind: ClusterRole + name: linkerd-heartbeat + apiGroup: rbac.authorization.k8s.io +subjects: +- kind: ServiceAccount + name: linkerd-heartbeat + namespace: {{.Release.Namespace}} +--- +kind: ServiceAccount +apiVersion: v1 +metadata: + name: linkerd-heartbeat + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: heartbeat + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +{{- include "partials.image-pull-secrets" .Values.imagePullSecrets }} +{{- end }} diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/templates/heartbeat.yaml b/charts/buoyant/linkerd-control-plane/2024.10.4/templates/heartbeat.yaml new file mode 100644 index 000000000..1f4249384 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/templates/heartbeat.yaml @@ -0,0 +1,101 @@ +{{ if not .Values.disableHeartBeat -}} +--- +### +### Heartbeat +### +apiVersion: batch/v1 +kind: CronJob +metadata: + name: linkerd-heartbeat + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: heartbeat + app.kubernetes.io/part-of: Linkerd + app.kubernetes.io/version: {{.Values.linkerdVersion}} + linkerd.io/control-plane-component: heartbeat + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +spec: + concurrencyPolicy: Replace + {{ if .Values.heartbeatSchedule -}} + schedule: "{{.Values.heartbeatSchedule}}" + {{ else -}} + schedule: "{{ dateInZone "04 15 * * *" (now | mustDateModify "+10m") "UTC"}}" + {{ end -}} + successfulJobsHistoryLimit: 0 + jobTemplate: + spec: + template: + metadata: + labels: + linkerd.io/control-plane-component: heartbeat + linkerd.io/workload-ns: {{.Release.Namespace}} + {{- with .Values.podLabels }}{{ toYaml . | trim | nindent 12 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} + {{- with .Values.podAnnotations }}{{ toYaml . | trim | nindent 12 }}{{- end }} + spec: + {{- if .Values.priorityClassName }} + priorityClassName: {{ .Values.priorityClassName }} + {{- end -}} + {{- with .Values.runtimeClassName }} + runtimeClassName: {{ . | quote }} + {{- end }} + {{- if .Values.tolerations -}} + {{- include "linkerd.tolerations" . | nindent 10 }} + {{- end -}} + {{- include "linkerd.node-selector" . | nindent 10 }} + securityContext: + seccompProfile: + type: RuntimeDefault + serviceAccountName: linkerd-heartbeat + restartPolicy: Never + automountServiceAccountToken: false + containers: + - name: heartbeat + image: {{.Values.controllerImage}}:{{.Values.controllerImageVersion | default .Values.linkerdVersion}} + imagePullPolicy: {{.Values.imagePullPolicy}} + env: + - name: LINKERD_DISABLED + value: "the heartbeat controller does not use the proxy" + {{- with (.Values.heartbeat).additionalEnv }} + {{- toYaml . | nindent 12 -}} + {{- end }} + {{- with (.Values.heartbeat).experimentalEnv }} + {{- toYaml . | nindent 12 -}} + {{- end }} + args: + - "heartbeat" + - "-controller-namespace={{.Release.Namespace}}" + - "-log-level={{.Values.controllerLogLevel}}" + - "-log-format={{.Values.controllerLogFormat}}" + {{- if .Values.prometheusUrl }} + - "-prometheus-url={{.Values.prometheusUrl}}" + {{- else }} + - "-prometheus-url=http://prometheus.linkerd-viz.svc.{{.Values.clusterDomain}}:9090" + {{- end }} + {{- if .Values.heartbeatResources -}} + {{- include "partials.resources" .Values.heartbeatResources | nindent 12 }} + {{- end }} + securityContext: + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: {{.Values.controllerUID}} + {{- if ge (int .Values.controllerGID) 0 }} + runAsGroup: {{.Values.controllerGID}} + {{- end }} + allowPrivilegeEscalation: false + seccompProfile: + type: RuntimeDefault + volumeMounts: + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + name: kube-api-access + readOnly: true + volumes: + - {{- include "partials.volumes.manual-mount-service-account-token" . | indent 12 | trimPrefix (repeat 11 " ") }} +{{- end }} diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/templates/identity-rbac.yaml b/charts/buoyant/linkerd-control-plane/2024.10.4/templates/identity-rbac.yaml new file mode 100644 index 000000000..6efdb4e10 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/templates/identity-rbac.yaml @@ -0,0 +1,49 @@ +--- +### +### Identity Controller Service RBAC +### +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: linkerd-{{.Release.Namespace}}-identity + labels: + linkerd.io/control-plane-component: identity + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +rules: +- apiGroups: ["authentication.k8s.io"] + resources: ["tokenreviews"] + verbs: ["create"] +# TODO(ver) Restrict this to the Linkerd namespace. See +# https://github.com/linkerd/linkerd2/issues/9367 +- apiGroups: [""] + resources: ["events"] + verbs: ["create", "patch"] +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: linkerd-{{.Release.Namespace}}-identity + labels: + linkerd.io/control-plane-component: identity + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: linkerd-{{.Release.Namespace}}-identity +subjects: +- kind: ServiceAccount + name: linkerd-identity + namespace: {{.Release.Namespace}} +--- +kind: ServiceAccount +apiVersion: v1 +metadata: + name: linkerd-identity + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: identity + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +{{- include "partials.image-pull-secrets" .Values.imagePullSecrets }} diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/templates/identity.yaml b/charts/buoyant/linkerd-control-plane/2024.10.4/templates/identity.yaml new file mode 100644 index 000000000..960cd9339 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/templates/identity.yaml @@ -0,0 +1,277 @@ +{{if .Values.identity -}} +--- +### +### Identity Controller Service +### +{{ if and (.Values.identity.issuer) (eq .Values.identity.issuer.scheme "linkerd.io/tls") -}} +kind: Secret +apiVersion: v1 +metadata: + name: linkerd-identity-issuer + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: identity + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +data: + crt.pem: {{b64enc (required "Please provide the identity issuer certificate" .Values.identity.issuer.tls.crtPEM | trim)}} + key.pem: {{b64enc (required "Please provide the identity issue private key" .Values.identity.issuer.tls.keyPEM | trim)}} +--- +{{- end}} +{{ if not (.Values.identity.externalCA) -}} +kind: ConfigMap +apiVersion: v1 +metadata: + name: linkerd-identity-trust-roots + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: identity + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +data: + ca-bundle.crt: |-{{.Values.identityTrustAnchorsPEM | trim | nindent 4}} +--- +{{- end}} +kind: Service +apiVersion: v1 +metadata: + name: linkerd-identity + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: identity + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +spec: + type: ClusterIP + selector: + linkerd.io/control-plane-component: identity + ports: + - name: grpc + port: 8080 + targetPort: 8080 +--- +kind: Service +apiVersion: v1 +metadata: + name: linkerd-identity-headless + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: identity + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +spec: + clusterIP: None + selector: + linkerd.io/control-plane-component: identity + ports: + - name: grpc + port: 8080 + targetPort: 8080 +--- +{{- if .Values.enablePodDisruptionBudget }} +kind: PodDisruptionBudget +apiVersion: policy/v1 +metadata: + name: linkerd-identity + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: identity + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +spec: + maxUnavailable: {{ .Values.controller.podDisruptionBudget.maxUnavailable }} + selector: + matchLabels: + linkerd.io/control-plane-component: identity +--- +{{- end }} +{{- $tree := deepCopy . }} +{{ $_ := set $tree.Values.proxy "workloadKind" "deployment" -}} +{{ $_ := set $tree.Values.proxy "component" "linkerd-identity" -}} +{{ $_ := set $tree.Values.proxy "waitBeforeExitSeconds" 0 -}} +{{- if not (empty .Values.identityProxyResources) }} +{{- $c := dig "cores" .Values.proxy.cores .Values.identityProxyResources }} +{{- $_ := set $tree.Values.proxy "cores" $c }} +{{- $r := merge .Values.identityProxyResources .Values.proxy.resources }} +{{- $_ := set $tree.Values.proxy "resources" $r }} +{{- end }} +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + {{ include "partials.annotations.created-by" . }} + labels: + app.kubernetes.io/name: identity + app.kubernetes.io/part-of: Linkerd + app.kubernetes.io/version: {{.Values.linkerdVersion}} + linkerd.io/control-plane-component: identity + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + name: linkerd-identity + namespace: {{ .Release.Namespace }} +spec: + replicas: {{.Values.controllerReplicas}} + revisionHistoryLimit: {{.Values.revisionHistoryLimit}} + selector: + matchLabels: + linkerd.io/control-plane-component: identity + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- include "partials.proxy.labels" $tree.Values.proxy | nindent 6}} + {{- if .Values.deploymentStrategy }} + strategy: + {{- with .Values.deploymentStrategy }}{{ toYaml . | trim | nindent 4 }}{{- end }} + {{- end }} + template: + metadata: + annotations: + {{ include "partials.annotations.created-by" . }} + {{- include "partials.proxy.annotations" . | nindent 8}} + {{- with .Values.podAnnotations }}{{ toYaml . | trim | nindent 8 }}{{- end }} + config.linkerd.io/default-inbound-policy: "all-unauthenticated" + labels: + linkerd.io/control-plane-component: identity + linkerd.io/control-plane-ns: {{.Release.Namespace}} + linkerd.io/workload-ns: {{.Release.Namespace}} + {{- include "partials.proxy.labels" $tree.Values.proxy | nindent 8}} + {{- with .Values.podLabels }}{{ toYaml . | trim | nindent 8 }}{{- end }} + spec: + {{- with .Values.runtimeClassName }} + runtimeClassName: {{ . | quote }} + {{- end }} + {{- if .Values.tolerations -}} + {{- include "linkerd.tolerations" . | nindent 6 }} + {{- end -}} + {{- include "linkerd.node-selector" . | nindent 6 }} + {{- $_ := set $tree "component" "identity" -}} + {{- include "linkerd.affinity" $tree | nindent 6 }} + automountServiceAccountToken: false + containers: + - args: + - identity + - -log-level={{.Values.controllerLogLevel}} + - -log-format={{.Values.controllerLogFormat}} + - -controller-namespace={{.Release.Namespace}} + - -identity-trust-domain={{.Values.identityTrustDomain | default .Values.clusterDomain}} + - -identity-issuance-lifetime={{.Values.identity.issuer.issuanceLifetime}} + - -identity-clock-skew-allowance={{.Values.identity.issuer.clockSkewAllowance}} + - -identity-scheme={{.Values.identity.issuer.scheme}} + - -enable-pprof={{.Values.enablePprof | default false}} + - -kube-apiclient-qps={{.Values.identity.kubeAPI.clientQPS}} + - -kube-apiclient-burst={{.Values.identity.kubeAPI.clientBurst}} + {{- include "partials.linkerd.trace" . | nindent 8 -}} + env: + - name: LINKERD_DISABLED + value: "linkerd-await cannot block the identity controller" + {{- with (.Values.identity).additionalEnv }} + {{- toYaml . | nindent 8 -}} + {{- end }} + {{- with (.Values.identity).experimentalEnv }} + {{- toYaml . | nindent 8 -}} + {{- end }} + image: {{.Values.controllerImage}}:{{.Values.controllerImageVersion | default .Values.linkerdVersion}} + imagePullPolicy: {{.Values.imagePullPolicy}} + livenessProbe: + httpGet: + path: /ping + port: 9990 + initialDelaySeconds: 10 + {{- with (.Values.identity.livenessProbe).timeoutSeconds }} + timeoutSeconds: {{ . }} + {{- end }} + name: identity + ports: + - containerPort: 8080 + name: grpc + - containerPort: 9990 + name: admin-http + readinessProbe: + failureThreshold: 7 + httpGet: + path: /ready + port: 9990 + {{- with (.Values.identity.readinessProbe).timeoutSeconds }} + timeoutSeconds: {{ . }} + {{- end }} + {{- if .Values.identityResources -}} + {{- include "partials.resources" .Values.identityResources | nindent 8 }} + {{- end }} + securityContext: + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: {{.Values.controllerUID}} + {{- if ge (int .Values.controllerGID) 0 }} + runAsGroup: {{.Values.controllerGID}} + {{- end }} + allowPrivilegeEscalation: false + seccompProfile: + type: RuntimeDefault + volumeMounts: + - mountPath: /var/run/linkerd/identity/issuer + name: identity-issuer + - mountPath: /var/run/linkerd/identity/trust-roots/ + name: trust-roots + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + name: kube-api-access + readOnly: true + {{- $_ := set $tree.Values.proxy "await" false }} + {{- $_ := set $tree.Values.proxy "loadTrustBundleFromConfigMap" true }} + {{- $_ := set $tree.Values.proxy "podInboundPorts" "8080,9990" }} + {{- $_ := set $tree.Values.proxy "nativeSidecar" false }} + {{- /* + The identity controller cannot discover policies, so we configure it with defaults that + enforce TLS on the identity service. + */}} + {{- $_ := set $tree.Values.proxy "defaultInboundPolicy" "all-unauthenticated" }} + {{- $_ := set $tree.Values.proxy "requireTLSOnInboundPorts" "8080" }} + {{- $_ := set $tree.Values.proxy "capabilities" (dict "drop" (list "ALL")) }} + {{- $_ := set $tree.Values.proxy "outboundDiscoveryCacheUnusedTimeout" "5s" }} + {{- $_ := set $tree.Values.proxy "inboundDiscoveryCacheUnusedTimeout" "90s" }} + - {{- include "partials.proxy" $tree | indent 8 | trimPrefix (repeat 7 " ") }} + initContainers: + {{ if .Values.cniEnabled -}} + - {{- include "partials.network-validator" $tree | indent 8 | trimPrefix (repeat 7 " ") }} + {{ else -}} + {{- /* + The identity controller needs to connect to the Kubernetes API before the proxy is able to + proxy requests, so we always skip these connections. The identity controller makes no other + outbound connections (so it's not important to persist any other skip ports here) + */}} + {{- $_ := set $tree.Values.proxyInit "ignoreOutboundPorts" .Values.proxyInit.kubeAPIServerPorts -}} + - {{- include "partials.proxy-init" $tree | indent 8 | trimPrefix (repeat 7 " ") }} + {{ end -}} + {{- if .Values.priorityClassName -}} + priorityClassName: {{ .Values.priorityClassName }} + {{ end -}} + securityContext: + seccompProfile: + type: RuntimeDefault + serviceAccountName: linkerd-identity + volumes: + - name: identity-issuer + secret: + secretName: linkerd-identity-issuer + - configMap: + name: linkerd-identity-trust-roots + name: trust-roots + - {{- include "partials.volumes.manual-mount-service-account-token" . | indent 8 | trimPrefix (repeat 7 " ") }} + {{ if not .Values.cniEnabled -}} + - {{- include "partials.proxyInit.volumes.xtables" . | indent 8 | trimPrefix (repeat 7 " ") }} + {{ end -}} + {{if .Values.identity.serviceAccountTokenProjection -}} + - {{- include "partials.proxy.volumes.service-account-token" . | indent 8 | trimPrefix (repeat 7 " ") }} + {{ end -}} + - {{- include "partials.proxy.volumes.identity" . | indent 8 | trimPrefix (repeat 7 " ") }} +{{end -}} diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/templates/namespace.yaml b/charts/buoyant/linkerd-control-plane/2024.10.4/templates/namespace.yaml new file mode 100644 index 000000000..61461c132 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/templates/namespace.yaml @@ -0,0 +1,18 @@ +{{- if eq .Release.Service "CLI" -}} +--- +### +### Linkerd Namespace +### +kind: Namespace +apiVersion: v1 +metadata: + name: {{ .Release.Namespace }} + annotations: + linkerd.io/inject: disabled + labels: + linkerd.io/is-control-plane: "true" + config.linkerd.io/admission-webhooks: disabled + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- /* linkerd-init requires extended capabilities and so requires priviledged mode */}} + pod-security.kubernetes.io/enforce: {{ ternary "restricted" "privileged" .Values.cniEnabled }} +{{ end -}} diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/templates/podmonitor.yaml b/charts/buoyant/linkerd-control-plane/2024.10.4/templates/podmonitor.yaml new file mode 100644 index 000000000..fd2b5d6ce --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/templates/podmonitor.yaml @@ -0,0 +1,128 @@ +{{- $podMonitor := .Values.podMonitor -}} +{{- if and $podMonitor.enabled $podMonitor.controller.enabled }} +--- +### +### Prometheus Operator PodMonitor for Linkerd control-plane +### +apiVersion: monitoring.coreos.com/v1 +kind: PodMonitor +metadata: + name: "linkerd-controller" + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-ns: {{ .Release.Namespace }} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + {{- with .Values.podMonitor.labels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +spec: + namespaceSelector: {{ tpl .Values.podMonitor.controller.namespaceSelector . | nindent 4 }} + selector: + matchLabels: {} + podMetricsEndpoints: + - interval: {{ $podMonitor.scrapeInterval }} + scrapeTimeout: {{ $podMonitor.scrapeTimeout }} + relabelings: + - sourceLabels: + - __meta_kubernetes_pod_container_port_name + action: keep + regex: admin-http + - sourceLabels: + - __meta_kubernetes_pod_container_name + action: replace + targetLabel: component +{{- end }} +{{- if and $podMonitor.enabled $podMonitor.serviceMirror.enabled }} +--- +### +### Prometheus Operator PodMonitor for Linkerd Service Mirror (multi-cluster) +### +apiVersion: monitoring.coreos.com/v1 +kind: PodMonitor +metadata: + name: "linkerd-service-mirror" + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-ns: {{ .Release.Namespace }} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + {{- with .Values.podMonitor.labels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +spec: + namespaceSelector: + any: true + selector: + matchLabels: {} + podMetricsEndpoints: + - interval: {{ $podMonitor.scrapeInterval }} + scrapeTimeout: {{ $podMonitor.scrapeTimeout }} + relabelings: + - sourceLabels: + - __meta_kubernetes_pod_label_linkerd_io_control_plane_component + - __meta_kubernetes_pod_container_port_name + action: keep + regex: linkerd-service-mirror;admin-http$ + - sourceLabels: + - __meta_kubernetes_pod_container_name + action: replace + targetLabel: component +{{- end }} +{{- if and $podMonitor.enabled $podMonitor.proxy.enabled }} +--- +### +### Prometheus Operator PodMonitor Linkerd data-plane +### +apiVersion: monitoring.coreos.com/v1 +kind: PodMonitor +metadata: + name: "linkerd-proxy" + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-ns: {{ .Release.Namespace }} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + {{- with .Values.podMonitor.labels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +spec: + namespaceSelector: + any: true + selector: + matchLabels: {} + podMetricsEndpoints: + - interval: {{ $podMonitor.scrapeInterval }} + scrapeTimeout: {{ $podMonitor.scrapeTimeout }} + relabelings: + - sourceLabels: + - __meta_kubernetes_pod_container_name + - __meta_kubernetes_pod_container_port_name + - __meta_kubernetes_pod_label_linkerd_io_control_plane_ns + action: keep + regex: ^linkerd-proxy;linkerd-admin;{{ .Release.Namespace }}$ + - sourceLabels: [ __meta_kubernetes_namespace ] + action: replace + targetLabel: namespace + - sourceLabels: [ __meta_kubernetes_pod_name ] + action: replace + targetLabel: pod + - sourceLabels: [ __meta_kubernetes_pod_label_linkerd_io_proxy_job ] + action: replace + targetLabel: k8s_job + - action: labeldrop + regex: __meta_kubernetes_pod_label_linkerd_io_proxy_job + - action: labelmap + regex: __meta_kubernetes_pod_label_linkerd_io_proxy_(.+) + - action: labeldrop + regex: __meta_kubernetes_pod_label_linkerd_io_proxy_(.+) + - action: labelmap + regex: __meta_kubernetes_pod_label_linkerd_io_(.+) + - action: labelmap + regex: __meta_kubernetes_pod_label_(.+) + replacement: __tmp_pod_label_$1 + - action: labelmap + regex: __tmp_pod_label_linkerd_io_(.+) + replacement: __tmp_pod_label_$1 + - action: labeldrop + regex: __tmp_pod_label_linkerd_io_(.+) + - action: labelmap + regex: __tmp_pod_label_(.+) +{{- end }} diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/templates/proxy-injector-rbac.yaml b/charts/buoyant/linkerd-control-plane/2024.10.4/templates/proxy-injector-rbac.yaml new file mode 100644 index 000000000..c2c84c5c1 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/templates/proxy-injector-rbac.yaml @@ -0,0 +1,120 @@ +--- +### +### Proxy Injector RBAC +### +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: linkerd-{{.Release.Namespace}}-proxy-injector + labels: + linkerd.io/control-plane-component: proxy-injector + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +rules: +- apiGroups: [""] + resources: ["events"] + verbs: ["create", "patch"] +- apiGroups: [""] + resources: ["namespaces", "replicationcontrollers"] + verbs: ["list", "get", "watch"] +- apiGroups: [""] + resources: ["pods"] + verbs: ["list", "watch"] +- apiGroups: ["extensions", "apps"] + resources: ["deployments", "replicasets", "daemonsets", "statefulsets"] + verbs: ["list", "get", "watch"] +- apiGroups: ["extensions", "batch"] + resources: ["cronjobs", "jobs"] + verbs: ["list", "get", "watch"] +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: linkerd-{{.Release.Namespace}}-proxy-injector + labels: + linkerd.io/control-plane-component: proxy-injector + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +subjects: +- kind: ServiceAccount + name: linkerd-proxy-injector + namespace: {{.Release.Namespace}} + apiGroup: "" +roleRef: + kind: ClusterRole + name: linkerd-{{.Release.Namespace}}-proxy-injector + apiGroup: rbac.authorization.k8s.io +--- +kind: ServiceAccount +apiVersion: v1 +metadata: + name: linkerd-proxy-injector + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: proxy-injector + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +{{- include "partials.image-pull-secrets" .Values.imagePullSecrets }} +--- +{{- $host := printf "linkerd-proxy-injector.%s.svc" .Release.Namespace }} +{{- $ca := genSelfSignedCert $host (list) (list $host) 365 }} +{{- if (not .Values.proxyInjector.externalSecret) }} +kind: Secret +apiVersion: v1 +metadata: + name: linkerd-proxy-injector-k8s-tls + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: proxy-injector + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +type: kubernetes.io/tls +data: + tls.crt: {{ ternary (b64enc (trim $ca.Cert)) (b64enc (trim .Values.proxyInjector.crtPEM)) (empty .Values.proxyInjector.crtPEM) }} + tls.key: {{ ternary (b64enc (trim $ca.Key)) (b64enc (trim .Values.proxyInjector.keyPEM)) (empty .Values.proxyInjector.keyPEM) }} +--- +{{- end }} +{{- include "linkerd.webhook.validation" .Values.proxyInjector }} +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + name: linkerd-proxy-injector-webhook-config + {{- if or (.Values.proxyInjector.injectCaFrom) (.Values.proxyInjector.injectCaFromSecret) }} + annotations: + {{- if .Values.proxyInjector.injectCaFrom }} + cert-manager.io/inject-ca-from: {{ .Values.proxyInjector.injectCaFrom }} + {{- end }} + {{- if .Values.proxyInjector.injectCaFromSecret }} + cert-manager.io/inject-ca-from-secret: {{ .Values.proxyInjector.injectCaFromSecret }} + {{- end }} + {{- end }} + labels: + linkerd.io/control-plane-component: proxy-injector + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +webhooks: +- name: linkerd-proxy-injector.linkerd.io + namespaceSelector: + {{- toYaml .Values.proxyInjector.namespaceSelector | trim | nindent 4 }} + objectSelector: + {{- toYaml .Values.proxyInjector.objectSelector | trim | nindent 4 }} + clientConfig: + service: + name: linkerd-proxy-injector + namespace: {{ .Release.Namespace }} + path: "/" + {{- if and (empty .Values.proxyInjector.injectCaFrom) (empty .Values.proxyInjector.injectCaFromSecret) }} + caBundle: {{ ternary (b64enc (trim $ca.Cert)) (b64enc (trim .Values.proxyInjector.caBundle)) (empty .Values.proxyInjector.caBundle) }} + {{- end }} + failurePolicy: {{.Values.webhookFailurePolicy}} + admissionReviewVersions: ["v1", "v1beta1"] + rules: + - operations: [ "CREATE" ] + apiGroups: [""] + apiVersions: ["v1"] + resources: ["pods", "services"] + scope: "Namespaced" + sideEffects: None + timeoutSeconds: {{ .Values.proxyInjector.timeoutSeconds | default 10 }} diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/templates/proxy-injector.yaml b/charts/buoyant/linkerd-control-plane/2024.10.4/templates/proxy-injector.yaml new file mode 100644 index 000000000..7d514dbf0 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/templates/proxy-injector.yaml @@ -0,0 +1,227 @@ +--- +### +### Proxy Injector +### +{{- $tree := deepCopy . }} +{{ $_ := set $tree.Values.proxy "workloadKind" "deployment" -}} +{{ $_ := set $tree.Values.proxy "component" "linkerd-proxy-injector" -}} +{{ $_ := set $tree.Values.proxy "waitBeforeExitSeconds" 0 -}} +{{- if not (empty .Values.proxyInjectorProxyResources) }} +{{- $c := dig "cores" .Values.proxy.cores .Values.proxyInjectorProxyResources }} +{{- $_ := set $tree.Values.proxy "cores" $c }} +{{- $r := merge .Values.proxyInjectorProxyResources .Values.proxy.resources }} +{{- $_ := set $tree.Values.proxy "resources" $r }} +{{- end }} +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + {{ include "partials.annotations.created-by" . }} + labels: + app.kubernetes.io/name: proxy-injector + app.kubernetes.io/part-of: Linkerd + app.kubernetes.io/version: {{.Values.linkerdVersion}} + linkerd.io/control-plane-component: proxy-injector + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + name: linkerd-proxy-injector + namespace: {{ .Release.Namespace }} +spec: + replicas: {{.Values.controllerReplicas}} + revisionHistoryLimit: {{.Values.revisionHistoryLimit}} + selector: + matchLabels: + linkerd.io/control-plane-component: proxy-injector + {{- if .Values.deploymentStrategy }} + strategy: + {{- with .Values.deploymentStrategy }}{{ toYaml . | trim | nindent 4 }}{{- end }} + {{- end }} + template: + metadata: + annotations: + checksum/config: {{ include (print $.Template.BasePath "/proxy-injector-rbac.yaml") . | sha256sum }} + {{ include "partials.annotations.created-by" . }} + {{- include "partials.proxy.annotations" . | nindent 8}} + {{- with .Values.podAnnotations }}{{ toYaml . | trim | nindent 8 }}{{- end }} + config.linkerd.io/opaque-ports: "8443" + config.linkerd.io/default-inbound-policy: "all-unauthenticated" + labels: + linkerd.io/control-plane-component: proxy-injector + linkerd.io/control-plane-ns: {{.Release.Namespace}} + linkerd.io/workload-ns: {{.Release.Namespace}} + {{- include "partials.proxy.labels" $tree.Values.proxy | nindent 8}} + {{- with .Values.podLabels }}{{ toYaml . | trim | nindent 8 }}{{- end }} + spec: + {{- with .Values.runtimeClassName }} + runtimeClassName: {{ . | quote }} + {{- end }} + {{- if .Values.tolerations -}} + {{- include "linkerd.tolerations" . | nindent 6 }} + {{- end -}} + {{- include "linkerd.node-selector" . | nindent 6 }} + {{- $_ := set $tree "component" "proxy-injector" -}} + {{- include "linkerd.affinity" $tree | nindent 6 }} + automountServiceAccountToken: false + containers: + {{- $_ := set $tree.Values.proxy "await" $tree.Values.proxy.await }} + {{- $_ := set $tree.Values.proxy "loadTrustBundleFromConfigMap" true }} + {{- $_ := set $tree.Values.proxy "podInboundPorts" "8443,9995" }} + {{- /* + The pod needs to accept webhook traffic, and we can't rely on that originating in the + cluster network. + */}} + {{- $_ := set $tree.Values.proxy "defaultInboundPolicy" "all-unauthenticated" }} + {{- $_ := set $tree.Values.proxy "capabilities" (dict "drop" (list "ALL")) }} + {{- $_ := set $tree.Values.proxy "outboundDiscoveryCacheUnusedTimeout" "5s" }} + {{- $_ := set $tree.Values.proxy "inboundDiscoveryCacheUnusedTimeout" "90s" }} + {{- if not $tree.Values.proxy.nativeSidecar }} + - {{- include "partials.proxy" $tree | indent 8 | trimPrefix (repeat 7 " ") }} + {{- end }} + - args: + - proxy-injector + - -log-level={{.Values.controllerLogLevel}} + - -log-format={{.Values.controllerLogFormat}} + - -linkerd-namespace={{.Release.Namespace}} + - -enable-pprof={{.Values.enablePprof | default false}} + {{- if or (.Values.proxyInjector).additionalEnv (.Values.proxyInjector).experimentalEnv }} + env: + {{- with (.Values.proxyInjector).additionalEnv }} + {{- toYaml . | nindent 8 -}} + {{- end }} + {{- with (.Values.proxyInjector).experimentalEnv }} + {{- toYaml . | nindent 8 -}} + {{- end }} + {{- end }} + image: {{.Values.controllerImage}}:{{.Values.controllerImageVersion | default .Values.linkerdVersion}} + imagePullPolicy: {{.Values.imagePullPolicy}} + livenessProbe: + httpGet: + path: /ping + port: 9995 + initialDelaySeconds: 10 + {{- with (.Values.proxyInjector.livenessProbe).timeoutSeconds }} + timeoutSeconds: {{ . }} + {{- end }} + name: proxy-injector + ports: + - containerPort: 8443 + name: proxy-injector + - containerPort: 9995 + name: admin-http + readinessProbe: + failureThreshold: 7 + httpGet: + path: /ready + port: 9995 + {{- with (.Values.proxyInjector.readinessProbe).timeoutSeconds }} + timeoutSeconds: {{ . }} + {{- end }} + {{- if .Values.proxyInjectorResources -}} + {{- include "partials.resources" .Values.proxyInjectorResources | nindent 8 }} + {{- end }} + securityContext: + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: {{.Values.controllerUID}} + {{- if ge (int .Values.controllerGID) 0 }} + runAsGroup: {{.Values.controllerGID}} + {{- end }} + allowPrivilegeEscalation: false + seccompProfile: + type: RuntimeDefault + volumeMounts: + - mountPath: /var/run/linkerd/config + name: config + - mountPath: /var/run/linkerd/identity/trust-roots + name: trust-roots + - mountPath: /var/run/linkerd/tls + name: tls + readOnly: true + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + name: kube-api-access + readOnly: true + initContainers: + {{ if .Values.cniEnabled -}} + - {{- include "partials.network-validator" $tree | indent 8 | trimPrefix (repeat 7 " ") }} + {{ else -}} + {{- /* + The controller needs to connect to the Kubernetes API. There's no reason + to put the proxy in the way of that. + */}} + {{- $_ := set $tree.Values.proxyInit "ignoreOutboundPorts" .Values.proxyInit.kubeAPIServerPorts -}} + - {{- include "partials.proxy-init" $tree | indent 8 | trimPrefix (repeat 7 " ") }} + {{ end -}} + {{- if $tree.Values.proxy.nativeSidecar }} + {{- $_ := set $tree.Values.proxy "startupProbeInitialDelaySeconds" 35 }} + {{- $_ := set $tree.Values.proxy "startupProbePeriodSeconds" 5 }} + {{- $_ := set $tree.Values.proxy "startupProbeFailureThreshold" 20 }} + - {{- include "partials.proxy" $tree | indent 8 | trimPrefix (repeat 7 " ") }} + {{ end -}} + {{- if .Values.priorityClassName -}} + priorityClassName: {{ .Values.priorityClassName }} + {{ end -}} + securityContext: + seccompProfile: + type: RuntimeDefault + serviceAccountName: linkerd-proxy-injector + volumes: + - configMap: + name: linkerd-config + name: config + - configMap: + name: linkerd-identity-trust-roots + name: trust-roots + - name: tls + secret: + secretName: linkerd-proxy-injector-k8s-tls + - {{- include "partials.volumes.manual-mount-service-account-token" . | indent 8 | trimPrefix (repeat 7 " ") }} + {{ if not .Values.cniEnabled -}} + - {{- include "partials.proxyInit.volumes.xtables" . | indent 8 | trimPrefix (repeat 7 " ") }} + {{ end -}} + {{if .Values.identity.serviceAccountTokenProjection -}} + - {{- include "partials.proxy.volumes.service-account-token" . | indent 8 | trimPrefix (repeat 7 " ") }} + {{ end -}} + - {{- include "partials.proxy.volumes.identity" . | indent 8 | trimPrefix (repeat 7 " ") }} +--- +kind: Service +apiVersion: v1 +metadata: + name: linkerd-proxy-injector + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: proxy-injector + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} + config.linkerd.io/opaque-ports: "443" +spec: + type: ClusterIP + selector: + linkerd.io/control-plane-component: proxy-injector + ports: + - name: proxy-injector + port: 443 + targetPort: proxy-injector +{{- if .Values.enablePodDisruptionBudget }} +--- +kind: PodDisruptionBudget +apiVersion: policy/v1 +metadata: + name: linkerd-proxy-injector + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: proxy-injector + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +spec: + maxUnavailable: {{ .Values.controller.podDisruptionBudget.maxUnavailable }} + selector: + matchLabels: + linkerd.io/control-plane-component: proxy-injector +{{- end }} diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/templates/psp.yaml b/charts/buoyant/linkerd-control-plane/2024.10.4/templates/psp.yaml new file mode 100644 index 000000000..db91fea67 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/templates/psp.yaml @@ -0,0 +1,119 @@ +{{ if .Values.enablePSP -}} +--- +### +### Control Plane PSP +### +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: linkerd-{{.Release.Namespace}}-control-plane + annotations: + seccomp.security.alpha.kubernetes.io/allowedProfileNames: "runtime/default" + labels: + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +spec: + {{- if or .Values.proxyInit.closeWaitTimeoutSecs .Values.proxyInit.runAsRoot }} + allowPrivilegeEscalation: true + {{- else }} + allowPrivilegeEscalation: false + {{- end }} + readOnlyRootFilesystem: true + {{- if empty .Values.cniEnabled }} + allowedCapabilities: + - NET_ADMIN + - NET_RAW + {{- end}} + requiredDropCapabilities: + - ALL + hostNetwork: false + hostIPC: false + hostPID: false + seLinux: + rule: RunAsAny + runAsUser: + {{- if .Values.cniEnabled }} + rule: MustRunAsNonRoot + {{- else }} + rule: RunAsAny + {{- end }} + runAsGroup: + {{- if .Values.cniEnabled }} + rule: MustRunAs + ranges: + - min: 1000 + max: 999999 + {{- else }} + rule: RunAsAny + {{- end }} + supplementalGroups: + rule: MustRunAs + ranges: + {{- if .Values.cniEnabled }} + - min: 10001 + max: 65535 + {{- else }} + - min: 1 + max: 65535 + {{- end }} + fsGroup: + rule: MustRunAs + ranges: + {{- if .Values.cniEnabled }} + - min: 10001 + max: 65535 + {{- else }} + - min: 1 + max: 65535 + {{- end }} + volumes: + - configMap + - emptyDir + - secret + - projected + - downwardAPI + - persistentVolumeClaim +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: linkerd-psp + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +rules: +- apiGroups: ['policy', 'extensions'] + resources: ['podsecuritypolicies'] + verbs: ['use'] + resourceNames: + - linkerd-{{.Release.Namespace}}-control-plane +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: linkerd-psp + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +roleRef: + kind: Role + name: linkerd-psp + apiGroup: rbac.authorization.k8s.io +subjects: +- kind: ServiceAccount + name: linkerd-destination + namespace: {{.Release.Namespace}} +{{ if not .Values.disableHeartBeat -}} +- kind: ServiceAccount + name: linkerd-heartbeat + namespace: {{.Release.Namespace}} +{{ end -}} +- kind: ServiceAccount + name: linkerd-identity + namespace: {{.Release.Namespace}} +- kind: ServiceAccount + name: linkerd-proxy-injector + namespace: {{.Release.Namespace}} +{{ end -}} diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/values-ha.yaml b/charts/buoyant/linkerd-control-plane/2024.10.4/values-ha.yaml new file mode 100644 index 000000000..e3b8cbc07 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/values-ha.yaml @@ -0,0 +1,63 @@ +# This values.yaml file contains the values needed to enable HA mode. +# Usage: +# helm install -f values-ha.yaml + +# -- Create PodDisruptionBudget resources for each control plane workload +enablePodDisruptionBudget: true + +controller: + # -- sets pod disruption budget parameter for all deployments + podDisruptionBudget: + # -- Maximum number of pods that can be unavailable during disruption + maxUnavailable: 1 + +# -- Specify a deployment strategy for each control plane workload +deploymentStrategy: + rollingUpdate: + maxUnavailable: 1 + maxSurge: 25% + +# -- add PodAntiAffinity to each control plane workload +enablePodAntiAffinity: true + +# nodeAffinity: + +# proxy configuration +proxy: + resources: + cpu: + request: 100m + memory: + limit: 250Mi + request: 20Mi + +# controller configuration +controllerReplicas: 3 +controllerResources: &controller_resources + cpu: &controller_resources_cpu + limit: "" + request: 100m + memory: + limit: 250Mi + request: 50Mi +destinationResources: *controller_resources + +# identity configuration +identityResources: + cpu: *controller_resources_cpu + memory: + limit: 250Mi + request: 10Mi + +# heartbeat configuration +heartbeatResources: *controller_resources + +# proxy injector configuration +proxyInjectorResources: *controller_resources +webhookFailurePolicy: Fail + +# service profile validator configuration +spValidatorResources: *controller_resources + +# flag for linkerd check +highAvailability: true diff --git a/charts/buoyant/linkerd-control-plane/2024.10.4/values.yaml b/charts/buoyant/linkerd-control-plane/2024.10.4/values.yaml new file mode 100644 index 000000000..b67e0308d --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2024.10.4/values.yaml @@ -0,0 +1,664 @@ +# Default values for linkerd. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +# -- Kubernetes DNS Domain name to use +clusterDomain: cluster.local + +# -- The cluster networks for which service discovery is performed. This should +# include the pod and service networks, but need not include the node network. +# +# By default, all IPv4 private networks and all accepted IPv6 ULAs are +# specified so that resolution works in typical Kubernetes environments. +clusterNetworks: "10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16,fd00::/8" +# -- Docker image pull policy +imagePullPolicy: IfNotPresent +# -- Specifies the number of old ReplicaSets to retain to allow rollback. +revisionHistoryLimit: 10 +# -- Log level for the control plane components +controllerLogLevel: info +# -- Log format for the control plane components +controllerLogFormat: plain +# -- enables control plane tracing +controlPlaneTracing: false +# -- namespace to send control plane traces to +controlPlaneTracingNamespace: linkerd-jaeger +# -- control plane version. See Proxy section for proxy version +linkerdVersion: edge-24.10.4 +# -- default kubernetes deployment strategy +deploymentStrategy: + rollingUpdate: + maxUnavailable: 25% + maxSurge: 25% +# -- enables the use of EndpointSlice informers for the destination service; +# enableEndpointSlices should be set to true only if EndpointSlice K8s feature +# gate is on +enableEndpointSlices: true +# -- enables pod anti affinity creation on deployments for high availability +enablePodAntiAffinity: false +# -- enables the use of pprof endpoints on control plane component's admin +# servers +enablePprof: false +# -- enables the creation of pod disruption budgets for control plane components +enablePodDisruptionBudget: false +# -- disables routing IPv6 traffic in addition to IPv4 traffic through the +# proxy (IPv6 routing only available as of proxy-init v2.3.0 and linkerd-cni +# v1.4.0) +disableIPv6: true + +controller: + # -- sets pod disruption budget parameter for all deployments + podDisruptionBudget: + # -- Maximum number of pods that can be unavailable during disruption + maxUnavailable: 1 +# -- enabling this omits the NET_ADMIN capability in the PSP +# and the proxy-init container when injecting the proxy; +# requires the linkerd-cni plugin to already be installed +cniEnabled: false +# -- Trust root certificate (ECDSA). It must be provided during install. +identityTrustAnchorsPEM: | +# -- Trust domain used for identity +# @default -- clusterDomain +identityTrustDomain: "" +kubeAPI: &kubeapi + # -- Maximum QPS sent to the kube-apiserver before throttling. + # See [token bucket rate limiter + # implementation](https://github.com/kubernetes/client-go/blob/v12.0.0/util/flowcontrol/throttle.go) + clientQPS: 100 + # -- Burst value over clientQPS + clientBurst: 200 +# -- Additional annotations to add to all pods +podAnnotations: {} +# -- Additional labels to add to all pods +podLabels: {} +# -- Labels to apply to all resources +commonLabels: {} +# -- Kubernetes priorityClassName for the Linkerd Pods +priorityClassName: "" +# -- Runtime Class Name for all the pods +runtimeClassName: "" + +# policy controller configuration +policyController: + image: + # -- Docker image for the policy controller + name: cr.l5d.io/linkerd/policy-controller + # -- Pull policy for the policy controller container image + # @default -- imagePullPolicy + pullPolicy: "" + # -- Tag for the policy controller container image + # @default -- linkerdVersion + version: "" + + # -- Log level for the policy controller + logLevel: info + + # -- The networks from which probes are performed. + # + # By default, all networks are allowed so that all probes are authorized. + probeNetworks: + - 0.0.0.0/0 + - "::/0" + + # -- policy controller resource requests & limits + resources: + cpu: + # -- Maximum amount of CPU units that the policy controller can use + limit: "" + # -- Amount of CPU units that the policy controller requests + request: "" + memory: + # -- Maximum amount of memory that the policy controller can use + limit: "" + # -- Maximum amount of memory that the policy controller requests + request: "" + ephemeral-storage: + # -- Maximum amount of ephemeral storage that the policy controller can use + limit: "" + # -- Amount of ephemeral storage that the policy controller requests + request: "" + + livenessProbe: + timeoutSeconds: 1 + readinessProbe: + timeoutSeconds: 1 + +# proxy configuration +proxy: + # -- Enable service profiles for non-Kubernetes services + enableExternalProfiles: false + # -- Maximum time allowed for the proxy to establish an outbound TCP + # connection + outboundConnectTimeout: 1000ms + # -- Maximum time allowed for the proxy to establish an inbound TCP + # connection + inboundConnectTimeout: 100ms + # -- Maximum time allowed before an unused outbound discovery result + # is evicted from the cache + outboundDiscoveryCacheUnusedTimeout: "5s" + # -- Maximum time allowed before an unused inbound discovery result + # is evicted from the cache + inboundDiscoveryCacheUnusedTimeout: "90s" + # -- When set to true, disables the protocol detection timeout on the + # outbound side of the proxy by setting it to a very high value + disableOutboundProtocolDetectTimeout: false + # -- When set to true, disables the protocol detection timeout on the inbound + # side of the proxy by setting it to a very high value + disableInboundProtocolDetectTimeout: false + image: + # -- Docker image for the proxy + name: cr.l5d.io/linkerd/proxy + # -- Pull policy for the proxy container image + # @default -- imagePullPolicy + pullPolicy: "" + # -- Tag for the proxy container image + # @default -- linkerdVersion + version: "" + # -- Enables the proxy's /shutdown admin endpoint + enableShutdownEndpoint: false + # -- Log level for the proxy + logLevel: warn,linkerd=info,hickory=error + # -- Log format (`plain` or `json`) for the proxy + logFormat: plain + # -- (`off` or `insecure`) If set to `off`, will prevent the proxy from + # logging HTTP headers. If set to `insecure`, HTTP headers may be logged + # verbatim. Note that setting this to `insecure` is not alone sufficient to + # log HTTP headers; the proxy logLevel must also be set to debug. + logHTTPHeaders: "off" + ports: + # -- Admin port for the proxy container + admin: 4191 + # -- Control port for the proxy container + control: 4190 + # -- Inbound port for the proxy container + inbound: 4143 + # -- Outbound port for the proxy container + outbound: 4140 + # -- The `cpu.limit` and `cores` should be kept in sync. The value of `cores` + # must be an integer and should typically be set by rounding up from the + # limit. E.g. if cpu.limit is '1500m', cores should be 2. + cores: 0 + resources: + cpu: + # -- Maximum amount of CPU units that the proxy can use + limit: "" + # -- Amount of CPU units that the proxy requests + request: "" + memory: + # -- Maximum amount of memory that the proxy can use + limit: "" + # -- Maximum amount of memory that the proxy requests + request: "" + ephemeral-storage: + # -- Maximum amount of ephemeral storage that the proxy can use + limit: "" + # -- Amount of ephemeral storage that the proxy requests + request: "" + # -- User id under which the proxy runs + uid: 2102 + # -- (int) Optional customisation of the group id under which the proxy runs (the group ID will be omitted if lower than 0) + gid: -1 + + # -- If set the injected proxy sidecars in the data plane will stay alive for + # at least the given period before receiving the SIGTERM signal from + # Kubernetes but no longer than the pod's `terminationGracePeriodSeconds`. + # See [Lifecycle + # hooks](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks) + # for more info on container lifecycle hooks. + waitBeforeExitSeconds: 0 + # -- If set, the application container will not start until the proxy is + # ready + await: true + requireIdentityOnInboundPorts: "" + # -- Default set of opaque ports + # - SMTP (25,587) server-first + # - MYSQL (3306) server-first + # - Galera (4444) server-first + # - PostgreSQL (5432) server-first + # - Redis (6379) server-first + # - ElasticSearch (9300) server-first + # - Memcached (11211) clients do not issue any preamble, which breaks detection + opaquePorts: "25,587,3306,4444,5432,6379,9300,11211" + # -- Grace period for graceful proxy shutdowns. If this timeout elapses before all open connections have completed, the proxy will terminate forcefully, closing any remaining connections. + shutdownGracePeriod: "" + # -- The default allow policy to use when no `Server` selects a pod. One of: "all-authenticated", + # "all-unauthenticated", "cluster-authenticated", "cluster-unauthenticated", "deny", "audit" + # @default -- "all-unauthenticated" + defaultInboundPolicy: "all-unauthenticated" + # -- Enable KEP-753 native sidecars + # This is an experimental feature. It requires Kubernetes >= 1.29. + # If enabled, .proxy.waitBeforeExitSeconds should not be used. + nativeSidecar: false + # -- Native sidecar proxy startup probe parameters. + # -- LivenessProbe timeout and delay configuration + livenessProbe: + initialDelaySeconds: 10 + timeoutSeconds: 1 + # -- ReadinessProbe timeout and delay configuration + readinessProbe: + initialDelaySeconds: 2 + timeoutSeconds: 1 + startupProbe: + initialDelaySeconds: 0 + periodSeconds: 1 + failureThreshold: 120 + # Configures general properties of the proxy's control plane clients. + control: + # Configures limits on API response streams. + streams: + # -- The timeout for the first update from the control plane. + initialTimeout: "3s" + # -- The timeout between consecutive updates from the control plane. + idleTimeout: "5m" + # -- The maximum duration for a response stream (i.e. before it will be + # reinitialized). + lifetime: "1h" + inbound: + server: + http2: + # -- The interval at which PINGs are issued to remote HTTP/2 clients. + keepAliveInterval: "10s" + # -- The timeout within which keep-alive PINGs must be acknowledged on inbound HTTP/2 connections. + keepAliveTimeout: "3s" + outbound: + server: + http2: + # -- The interval at which PINGs are issued to local application HTTP/2 clients. + keepAliveInterval: "10s" + # -- The timeout within which keep-alive PINGs must be acknowledged on outbound HTTP/2 connections. + keepAliveTimeout: "3s" + +# proxy-init configuration +proxyInit: + # -- Variant of iptables that will be used to configure routing. Currently, + # proxy-init can be run either in 'nft' or in 'legacy' mode. The mode will + # control which utility binary will be called. The host must support + # whichever mode will be used + iptablesMode: "legacy" + # -- Default set of inbound ports to skip via iptables + # - Galera (4567,4568) + ignoreInboundPorts: "4567,4568" + # -- Default set of outbound ports to skip via iptables + # - Galera (4567,4568) + ignoreOutboundPorts: "4567,4568" + # -- Default set of ports to skip via iptables for control plane + # components so they can communicate with the Kubernetes API Server + kubeAPIServerPorts: "443,6443" + # -- Comma-separated list of subnets in valid CIDR format that should be skipped by the proxy + skipSubnets: "" + # -- Log level for the proxy-init + # @default -- info + logLevel: "" + # -- Log format (`plain` or `json`) for the proxy-init + # @default -- plain + logFormat: "" + image: + # -- Docker image for the proxy-init container + name: cr.l5d.io/linkerd/proxy-init + # -- Pull policy for the proxy-init container image + # @default -- imagePullPolicy + pullPolicy: "" + # -- Tag for the proxy-init container image + version: v2.4.1 + closeWaitTimeoutSecs: 0 + # -- Privileged mode allows the container processes to inherit all security + # capabilities and bypass any security limitations enforced by the kubelet. + # When used with 'runAsRoot: true', the container will behave exactly as if + # it was running as root on the host. May escape cgroup limits and see other + # processes and devices on the host. + # @default -- false + privileged: false + # -- Allow overriding the runAsNonRoot behaviour () + runAsRoot: false + # -- This value is used only if runAsRoot is false; otherwise runAsUser will be 0 + runAsUser: 65534 + # -- This value is used only if runAsRoot is false; otherwise runAsGroup will be 0 + runAsGroup: 65534 + xtMountPath: + mountPath: /run + name: linkerd-proxy-init-xtables-lock + +# network validator configuration +# This runs on a host that uses iptables to reroute network traffic. The validator +# ensures that iptables is correctly routing requests before we start linkerd. +networkValidator: + # -- Log level for the network-validator + # @default -- debug + logLevel: debug + # -- Log format (`plain` or `json`) for network-validator + # @default -- plain + logFormat: plain + # -- Address to which the network-validator will attempt to connect. This should be an IP + # that the cluster is expected to be able to reach but a port it should not, e.g., a public IP + # for public clusters and a private IP for air-gapped clusters with a port like 20001. + # If empty, defaults to 1.1.1.1:20001 and [fd00::1]:20001 for IPv4 and IPv6 respectively. + connectAddr: "" + # -- Address to which network-validator listens to requests from itself. + # If empty, defaults to 0.0.0.0:4140 and [::]:4140 for IPv4 and IPv6 respectively. + listenAddr: "" + # -- Timeout before network-validator fails to validate the pod's network connectivity + timeout: "10s" + # -- Include a securityContext in the network-validator pod spec + enableSecurityContext: true + +# -- For Private docker registries, authentication is needed. +# Registry secrets are applied to the respective service accounts +imagePullSecrets: [] +# - name: my-private-docker-registry-login-secret + +# -- Allow proxies to perform transparent HTTP/2 upgrading +enableH2Upgrade: true + +# -- Add a PSP resource and bind it to the control plane ServiceAccounts. Note +# PSP has been deprecated since k8s v1.21 +enablePSP: false + +# -- Failure policy for the proxy injector +webhookFailurePolicy: Ignore + +# controllerImage -- Docker image for the destination and identity components +controllerImage: cr.l5d.io/linkerd/controller +# -- Optionally allow a specific container image Tag (or SHA) to be specified for the controllerImage. +controllerImageVersion: "" + +# -- Number of replicas for each control plane pod +controllerReplicas: 1 +# -- User ID for the control plane components +controllerUID: 2103 +# -- (int) Optional customisation of the group ID for the control plane components (the group ID will be omitted if lower than 0) +controllerGID: -1 + +# destination configuration +# set resources for the sp-validator and its linkerd proxy respectively +# see proxy.resources for details. +# destinationResources -- CPU, Memory and Ephemeral Storage resources required by destination (see `proxy.resources` for sub-fields) +#destinationResources: +# destinationProxyResources -- CPU, Memory and Ephemeral Storage resources required by proxy injected into destination pod (see `proxy.resources` for sub-fields) +#destinationProxyResources: + +destinationController: + meshedHttp2ClientProtobuf: + keep_alive: + interval: + seconds: 10 + timeout: + seconds: 3 + while_idle: true + livenessProbe: + timeoutSeconds: 1 + readinessProbe: + timeoutSeconds: 1 + +# debug configuration +debugContainer: + image: + # -- Docker image for the debug container + name: cr.l5d.io/linkerd/debug + # -- Pull policy for the debug container image + # @default -- imagePullPolicy + pullPolicy: "" + # -- Tag for the debug container image + # @default -- linkerdVersion + version: "" + +identity: + # -- If the linkerd-identity-trust-roots ConfigMap has already been created + externalCA: false + + # -- Use [Service Account token Volume projection](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#service-account-token-volume-projection) for pod validation instead of the default token + serviceAccountTokenProjection: true + + issuer: + scheme: linkerd.io/tls + + # -- Amount of time to allow for clock skew within a Linkerd cluster + clockSkewAllowance: 20s + + # -- Amount of time for which the Identity issuer should certify identity + issuanceLifetime: 24h0m0s + + # -- Which scheme is used for the identity issuer secret format + tls: + # -- Issuer certificate (ECDSA). It must be provided during install. + crtPEM: | + + # -- Key for the issuer certificate (ECDSA). It must be provided during + # install + keyPEM: | + + kubeAPI: *kubeapi + + livenessProbe: + timeoutSeconds: 1 + readinessProbe: + timeoutSeconds: 1 + +# -|- CPU, Memory and Ephemeral Storage resources required by the identity controller (see `proxy.resources` for sub-fields) +#identityResources: +# -|- CPU, Memory and Ephemeral Storage resources required by proxy injected into identity pod (see `proxy.resources` for sub-fields) +#identityProxyResources: + +# heartbeat configuration +# disableHeartBeat -- Set to true to not start the heartbeat cronjob +disableHeartBeat: false +# -- Config for the heartbeat cronjob +# heartbeatSchedule: "0 0 * * *" + +# proxy injector configuration +proxyInjector: + # -- Timeout in seconds before the API Server cancels a request to the proxy + # injector. If timeout is exceeded, the webhookfailurePolicy is used. + timeoutSeconds: 10 + # -- Do not create a secret resource for the proxyInjector webhook. + # If this is set to `true`, the value `proxyInjector.caBundle` must be set + # or the ca bundle must injected with cert-manager ca injector using + # `proxyInjector.injectCaFrom` or `proxyInjector.injectCaFromSecret` (see below). + externalSecret: false + + # -- Namespace selector used by admission webhook. + namespaceSelector: + matchExpressions: + - key: config.linkerd.io/admission-webhooks + operator: NotIn + values: + - disabled + - key: kubernetes.io/metadata.name + operator: NotIn + values: + - kube-system + - cert-manager + + # -- Object selector used by admission webhook. + objectSelector: + matchExpressions: + - key: linkerd.io/control-plane-component + operator: DoesNotExist + - key: linkerd.io/cni-resource + operator: DoesNotExist + + # -- Certificate for the proxy injector. If not provided and not using an external secret + # then Helm will generate one. + crtPEM: | + + # -- Certificate key for the proxy injector. If not provided and not using an external secret + # then Helm will generate one. + keyPEM: | + + # -- Bundle of CA certificates for proxy injector. + # If not provided nor injected with cert-manager, + # then Helm will use the certificate generated for `proxyInjector.crtPEM`. + # If `proxyInjector.externalSecret` is set to true, this value, injectCaFrom, or + # injectCaFromSecret must be set, as no certificate will be generated. + # See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector) for more information. + caBundle: | + + # -- Inject the CA bundle from a cert-manager Certificate. + # See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector/#injecting-ca-data-from-a-certificate-resource) + # for more information. + injectCaFrom: "" + + # -- Inject the CA bundle from a Secret. + # If set, the `cert-manager.io/inject-ca-from-secret` annotation will be added to the webhook. + # The Secret must have the CA Bundle stored in the `ca.crt` key and have + # the `cert-manager.io/allow-direct-injection` annotation set to `true`. + # See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector/#injecting-ca-data-from-a-secret-resource) + # for more information. + injectCaFromSecret: "" + + livenessProbe: + timeoutSeconds: 1 + readinessProbe: + timeoutSeconds: 1 + +# -|- CPU, Memory and Ephemeral Storage resources required by the proxy injector (see +#`proxy.resources` for sub-fields) +#proxyInjectorResources: +#-|- CPU, Memory and Ephemeral Storage resources required by proxy injected into the proxy injector +#pod (see `proxy.resources` for sub-fields) +#proxyInjectorProxyResources: + +# service profile validator configuration +profileValidator: + # -- Do not create a secret resource for the profileValidator webhook. + # If this is set to `true`, the value `proxyInjector.caBundle` must be set + # or the ca bundle must injected with cert-manager ca injector using + # `proxyInjector.injectCaFrom` or `proxyInjector.injectCaFromSecret` (see below). + externalSecret: false + + # -- Namespace selector used by admission webhook + namespaceSelector: + matchExpressions: + - key: config.linkerd.io/admission-webhooks + operator: NotIn + values: + - disabled + + # -- Certificate for the service profile validator. If not provided and not using an external secret + # then Helm will generate one. + crtPEM: | + + # -- Certificate key for the service profile validator. If not provided and not using an external secret + # then Helm will generate one. + keyPEM: | + + # -- Bundle of CA certificates for proxy injector. + # If not provided nor injected with cert-manager, + # then Helm will use the certificate generated for `profileValidator.crtPEM`. + # If `profileValidator.externalSecret` is set to true, this value, injectCaFrom, or + # injectCaFromSecret must be set, as no certificate will be generated. + # See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector) for more information. + caBundle: | + + # -- Inject the CA bundle from a cert-manager Certificate. + # See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector/#injecting-ca-data-from-a-certificate-resource) + # for more information. + injectCaFrom: "" + + # -- Inject the CA bundle from a Secret. + # If set, the `cert-manager.io/inject-ca-from-secret` annotation will be added to the webhook. + # The Secret must have the CA Bundle stored in the `ca.crt` key and have + # the `cert-manager.io/allow-direct-injection` annotation set to `true`. + # See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector/#injecting-ca-data-from-a-secret-resource) + # for more information. + injectCaFromSecret: "" + +# policy validator configuration +policyValidator: + # -- Do not create a secret resource for the policyValidator webhook. + # If this is set to `true`, the value `policyValidator.caBundle` must be set + # or the ca bundle must injected with cert-manager ca injector using + # `policyValidator.injectCaFrom` or `policyValidator.injectCaFromSecret` (see below). + externalSecret: false + + # -- Namespace selector used by admission webhook + namespaceSelector: + matchExpressions: + - key: config.linkerd.io/admission-webhooks + operator: NotIn + values: + - disabled + + # -- Certificate for the policy validator. If not provided and not using an external secret + # then Helm will generate one. + crtPEM: | + + # -- Certificate key for the policy validator. If not provided and not using an external secret + # then Helm will generate one. + keyPEM: | + + # -- Bundle of CA certificates for proxy injector. + # If not provided nor injected with cert-manager, + # then Helm will use the certificate generated for `policyValidator.crtPEM`. + # If `policyValidator.externalSecret` is set to true, this value, injectCaFrom, or + # injectCaFromSecret must be set, as no certificate will be generated. + # See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector) for more information. + caBundle: | + + # -- Inject the CA bundle from a cert-manager Certificate. + # See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector/#injecting-ca-data-from-a-certificate-resource) + # for more information. + injectCaFrom: "" + + # -- Inject the CA bundle from a Secret. + # If set, the `cert-manager.io/inject-ca-from-secret` annotation will be added to the webhook. + # The Secret must have the CA Bundle stored in the `ca.crt` key and have + # the `cert-manager.io/allow-direct-injection` annotation set to `true`. + # See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector/#injecting-ca-data-from-a-secret-resource) + # for more information. + injectCaFromSecret: "" + +# -- NodeSelector section, See the [K8S +# documentation](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) +# for more information +nodeSelector: + kubernetes.io/os: linux + +# -- SP validator configuration +spValidator: + livenessProbe: + timeoutSeconds: 1 + readinessProbe: + timeoutSeconds: 1 + +# -|- CPU, Memory and Ephemeral Storage resources required by the SP validator (see +#`proxy.resources` for sub-fields) +#spValidatorResources: + +# -|- Tolerations section, See the +# [K8S documentation](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) +# for more information +#tolerations: + +# -|- NodeAffinity section, See the +# [K8S documentation](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity) +# for more information +#nodeAffinity: + +# -- url of external prometheus instance (used for the heartbeat) +prometheusUrl: "" + +# Prometheus Operator PodMonitor configuration +podMonitor: + # -- Enables the creation of Prometheus Operator [PodMonitor](https://prometheus-operator.dev/docs/operator/api/#monitoring.coreos.com/v1.PodMonitor) + enabled: false + # -- Interval at which metrics should be scraped + scrapeInterval: 10s + # -- Iimeout after which the scrape is ended + scrapeTimeout: 10s + # -- Labels to apply to all pod Monitors + labels: {} + controller: + # -- Enables the creation of PodMonitor for the control-plane + enabled: true + # -- Selector to select which namespaces the Endpoints objects are discovered from + namespaceSelector: | + matchNames: + - {{ .Release.Namespace }} + - linkerd-viz + - linkerd-jaeger + serviceMirror: + # -- Enables the creation of PodMonitor for the Service Mirror component + enabled: true + proxy: + # -- Enables the creation of PodMonitor for the data-plane + enabled: true diff --git a/charts/buoyant/linkerd-crds/2024.10.4/.helmignore b/charts/buoyant/linkerd-crds/2024.10.4/.helmignore new file mode 100644 index 000000000..79c90a806 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/.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 +OWNERS +# 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/buoyant/linkerd-crds/2024.10.4/Chart.lock b/charts/buoyant/linkerd-crds/2024.10.4/Chart.lock new file mode 100644 index 000000000..a62a03063 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: partials + repository: file://../partials + version: 0.1.0 +digest: sha256:8e42f9c9d4a2dc883f17f94d6044c97518ced19ad0922f47b8760e47135369ba +generated: "2021-08-17T10:42:52.610449255-05:00" diff --git a/charts/buoyant/linkerd-crds/2024.10.4/Chart.yaml b/charts/buoyant/linkerd-crds/2024.10.4/Chart.yaml new file mode 100644 index 000000000..50ff87c6a --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/Chart.yaml @@ -0,0 +1,26 @@ +annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Linkerd CRDs + catalog.cattle.io/kube-version: '>=1.22.0-0' + catalog.cattle.io/release-name: linkerd-crds +apiVersion: v2 +dependencies: +- name: partials + repository: file://../partials + version: 0.1.0 +description: 'Linkerd gives you observability, reliability, and security for your + microservices — with no code change required. ' +home: https://linkerd.io +icon: file://assets/icons/linkerd-crds.png +keywords: +- service-mesh +kubeVersion: '>=1.22.0-0' +maintainers: +- email: cncf-linkerd-dev@lists.cncf.io + name: Linkerd authors + url: https://linkerd.io/ +name: linkerd-crds +sources: +- https://github.com/linkerd/linkerd2/ +type: application +version: 2024.10.4 diff --git a/charts/buoyant/linkerd-crds/2024.10.4/README.md b/charts/buoyant/linkerd-crds/2024.10.4/README.md new file mode 100644 index 000000000..feb081cd5 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/README.md @@ -0,0 +1,73 @@ +# linkerd-crds + +Linkerd gives you observability, reliability, and security +for your microservices — with no code change required. + +![Version: 2024.10.4](https://img.shields.io/badge/Version-2024.10.4-informational?style=flat-square) +![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) + +**Homepage:** + +## Quickstart and documentation + +You can run Linkerd on any Kubernetes cluster in a matter of seconds. See the +[Linkerd Getting Started Guide][getting-started] for how. + +For more comprehensive documentation, start with the [Linkerd +docs][linkerd-docs]. + +## Adding Linkerd's Helm repository + +```bash +# To add the repo for Linkerd edge releases: +helm repo add linkerd https://helm.linkerd.io/edge +``` + +## Installing the linkerd-crds chart + +This installs the `linkerd-crds` chart, which only persists the CRDs that +Linkerd requires. + +After installing this chart, you need then to install the +`linkerd-control-plane` chart in the same namespace, which provides all the +linkerd core control components. + +```bash +helm install linkerd-crds -n linkerd --create-namespace linkerd/linkerd-crds +``` + +## Get involved + +* Check out Linkerd's source code at [GitHub][linkerd2]. +* Join Linkerd's [user mailing list][linkerd-users], [developer mailing + list][linkerd-dev], and [announcements mailing list][linkerd-announce]. +* Follow [@linkerd][twitter] on Twitter. +* Join the [Linkerd Slack][slack]. + +[getting-started]: https://linkerd.io/2/getting-started/ +[linkerd2]: https://github.com/linkerd/linkerd2 +[linkerd-announce]: https://lists.cncf.io/g/cncf-linkerd-announce +[linkerd-dev]: https://lists.cncf.io/g/cncf-linkerd-dev +[linkerd-docs]: https://linkerd.io/2/overview/ +[linkerd-users]: https://lists.cncf.io/g/cncf-linkerd-users +[slack]: http://slack.linkerd.io +[twitter]: https://twitter.com/linkerd + +## Requirements + +Kubernetes: `>=1.22.0-0` + +| Repository | Name | Version | +|------------|------|---------| +| file://../partials | partials | 0.1.0 | + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| enableHttpRoutes | bool | `true` | | +| enableTcpRoutes | bool | `true` | | +| enableTlsRoutes | bool | `true` | | + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.12.0](https://github.com/norwoodj/helm-docs/releases/v1.12.0) diff --git a/charts/buoyant/linkerd-crds/2024.10.4/README.md.gotmpl b/charts/buoyant/linkerd-crds/2024.10.4/README.md.gotmpl new file mode 100644 index 000000000..88be73954 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/README.md.gotmpl @@ -0,0 +1,59 @@ +{{ template "chart.header" . }} +{{ template "chart.description" . }} + +{{ template "chart.versionBadge" . }} +{{ template "chart.typeBadge" . }} +{{ template "chart.appVersionBadge" . }} + +{{ template "chart.homepageLine" . }} + +## Quickstart and documentation + +You can run Linkerd on any Kubernetes cluster in a matter of seconds. See the +[Linkerd Getting Started Guide][getting-started] for how. + +For more comprehensive documentation, start with the [Linkerd +docs][linkerd-docs]. + +## Adding Linkerd's Helm repository + +```bash +# To add the repo for Linkerd edge releases: +helm repo add linkerd https://helm.linkerd.io/edge +``` + +## Installing the linkerd-crds chart + +This installs the `linkerd-crds` chart, which only persists the CRDs that +Linkerd requires. + +After installing this chart, you need then to install the +`linkerd-control-plane` chart in the same namespace, which provides all the +linkerd core control components. + +```bash +helm install linkerd-crds -n linkerd --create-namespace linkerd/linkerd-crds +``` + +## Get involved + +* Check out Linkerd's source code at [GitHub][linkerd2]. +* Join Linkerd's [user mailing list][linkerd-users], [developer mailing + list][linkerd-dev], and [announcements mailing list][linkerd-announce]. +* Follow [@linkerd][twitter] on Twitter. +* Join the [Linkerd Slack][slack]. + +[getting-started]: https://linkerd.io/2/getting-started/ +[linkerd2]: https://github.com/linkerd/linkerd2 +[linkerd-announce]: https://lists.cncf.io/g/cncf-linkerd-announce +[linkerd-dev]: https://lists.cncf.io/g/cncf-linkerd-dev +[linkerd-docs]: https://linkerd.io/2/overview/ +[linkerd-users]: https://lists.cncf.io/g/cncf-linkerd-users +[slack]: http://slack.linkerd.io +[twitter]: https://twitter.com/linkerd + +{{ template "chart.requirementsSection" . }} + +{{ template "chart.valuesSection" . }} + +{{ template "helm-docs.versionFooter" . }} diff --git a/charts/buoyant/linkerd-crds/2024.10.4/app-readme.md b/charts/buoyant/linkerd-crds/2024.10.4/app-readme.md new file mode 100644 index 000000000..59010a6b2 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/app-readme.md @@ -0,0 +1,9 @@ +# Linkerd 2 CRDs Chart + +Linkerd is an ultra light, ultra simple, ultra powerful service mesh. Linkerd +adds security, observability, and reliability to Kubernetes, without the +complexity. + +This particular Helm chart only installs Linkerd CRDs. + +Full documentation available at: https://linkerd.io/2/overview/ diff --git a/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/.helmignore b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/.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/buoyant/linkerd-crds/2024.10.4/charts/partials/Chart.yaml b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/Chart.yaml new file mode 100644 index 000000000..23cfc167e --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +description: 'A Helm chart containing Linkerd partial templates, depended by the ''linkerd'' + and ''patch'' charts. ' +name: partials +version: 0.1.0 diff --git a/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/README.md b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/README.md new file mode 100644 index 000000000..10805c9b9 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/README.md @@ -0,0 +1,9 @@ +# partials + +A Helm chart containing Linkerd partial templates, +depended by the 'linkerd' and 'patch' charts. + +![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.12.0](https://github.com/norwoodj/helm-docs/releases/v1.12.0) diff --git a/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/README.md.gotmpl b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/README.md.gotmpl new file mode 100644 index 000000000..37f510106 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/README.md.gotmpl @@ -0,0 +1,14 @@ +{{ template "chart.header" . }} +{{ template "chart.description" . }} + +{{ template "chart.versionBadge" . }} +{{ template "chart.typeBadge" . }} +{{ template "chart.appVersionBadge" . }} + +{{ template "chart.homepageLine" . }} + +{{ template "chart.requirementsSection" . }} + +{{ template "chart.valuesSection" . }} + +{{ template "helm-docs.versionFooter" . }} diff --git a/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/NOTES.txt b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/NOTES.txt new file mode 100644 index 000000000..e69de29bb diff --git a/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_affinity.tpl b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_affinity.tpl new file mode 100644 index 000000000..5dde1da47 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_affinity.tpl @@ -0,0 +1,38 @@ +{{ define "linkerd.pod-affinity" -}} +podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchExpressions: + - key: {{ default "linkerd.io/control-plane-component" .label }} + operator: In + values: + - {{ .component }} + topologyKey: topology.kubernetes.io/zone + weight: 100 + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: {{ default "linkerd.io/control-plane-component" .label }} + operator: In + values: + - {{ .component }} + topologyKey: kubernetes.io/hostname +{{- end }} + +{{ define "linkerd.node-affinity" -}} +nodeAffinity: +{{- toYaml .Values.nodeAffinity | trim | nindent 2 }} +{{- end }} + +{{ define "linkerd.affinity" -}} +{{- if or .Values.enablePodAntiAffinity .Values.nodeAffinity -}} +affinity: +{{- end }} +{{- if .Values.enablePodAntiAffinity -}} +{{- include "linkerd.pod-affinity" . | nindent 2 }} +{{- end }} +{{- if .Values.nodeAffinity -}} +{{- include "linkerd.node-affinity" . | nindent 2 }} +{{- end }} +{{- end }} diff --git a/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_capabilities.tpl b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_capabilities.tpl new file mode 100644 index 000000000..a595d74c1 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_capabilities.tpl @@ -0,0 +1,16 @@ +{{- define "partials.proxy.capabilities" -}} +capabilities: + {{- if .Values.proxy.capabilities.add }} + add: + {{- toYaml .Values.proxy.capabilities.add | trim | nindent 4 }} + {{- end }} + {{- if .Values.proxy.capabilities.drop }} + drop: + {{- toYaml .Values.proxy.capabilities.drop | trim | nindent 4 }} + {{- end }} +{{- end -}} + +{{- define "partials.proxy-init.capabilities.drop" -}} +drop: +{{ toYaml .Values.proxyInit.capabilities.drop | trim }} +{{- end -}} diff --git a/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_debug.tpl b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_debug.tpl new file mode 100644 index 000000000..4df8cc77b --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_debug.tpl @@ -0,0 +1,15 @@ +{{- define "partials.debug" -}} +image: {{.Values.debugContainer.image.name}}:{{.Values.debugContainer.image.version | default .Values.linkerdVersion}} +imagePullPolicy: {{.Values.debugContainer.image.pullPolicy | default .Values.imagePullPolicy}} +name: linkerd-debug +terminationMessagePolicy: FallbackToLogsOnError +# some environments require probes, so we provide some infallible ones +livenessProbe: + exec: + command: + - "true" +readinessProbe: + exec: + command: + - "true" +{{- end -}} diff --git a/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_helpers.tpl b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_helpers.tpl new file mode 100644 index 000000000..b6cdc34d0 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_helpers.tpl @@ -0,0 +1,14 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Splits a coma separated list into a list of string values. +For example "11,22,55,44" will become "11","22","55","44" +*/}} +{{- define "partials.splitStringList" -}} +{{- if gt (len (toString .)) 0 -}} +{{- $ports := toString . | splitList "," -}} +{{- $last := sub (len $ports) 1 -}} +{{- range $i,$port := $ports -}} +"{{$port}}"{{ternary "," "" (ne $i $last)}} +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_metadata.tpl b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_metadata.tpl new file mode 100644 index 000000000..04d2f1bea --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_metadata.tpl @@ -0,0 +1,17 @@ +{{- define "partials.annotations.created-by" -}} +linkerd.io/created-by: {{ .Values.cliVersion | default (printf "linkerd/helm %s" ( (.Values.image).version | default .Values.linkerdVersion)) }} +{{- end -}} + +{{- define "partials.proxy.annotations" -}} +linkerd.io/proxy-version: {{.Values.proxy.image.version | default .Values.linkerdVersion}} +cluster-autoscaler.kubernetes.io/safe-to-evict: "true" +linkerd.io/trust-root-sha256: {{ .Values.identityTrustAnchorsPEM | sha256sum }} +{{- end -}} + +{{/* +To add labels to the control-plane components, instead update at individual component manifests as +adding here would also update `spec.selector.matchLabels` which are immutable and would fail upgrades. +*/}} +{{- define "partials.proxy.labels" -}} +linkerd.io/proxy-{{.workloadKind}}: {{.component}} +{{- end -}} diff --git a/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_network-validator.tpl b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_network-validator.tpl new file mode 100644 index 000000000..276056395 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_network-validator.tpl @@ -0,0 +1,45 @@ +{{- define "partials.network-validator" -}} +name: linkerd-network-validator +image: {{.Values.proxy.image.name}}:{{.Values.proxy.image.version | default .Values.linkerdVersion }} +imagePullPolicy: {{.Values.proxy.image.pullPolicy | default .Values.imagePullPolicy}} +{{ include "partials.resources" .Values.proxy.resources }} +{{- if or .Values.networkValidator.enableSecurityContext }} +securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsGroup: 65534 + runAsNonRoot: true + runAsUser: 65534 + seccompProfile: + type: RuntimeDefault +{{- end }} +command: + - /usr/lib/linkerd/linkerd2-network-validator +args: + - --log-format + - {{ .Values.networkValidator.logFormat }} + - --log-level + - {{ .Values.networkValidator.logLevel }} + - --connect-addr + {{- if .Values.networkValidator.connectAddr }} + - {{ .Values.networkValidator.connectAddr | quote }} + {{- else if .Values.disableIPv6}} + - "1.1.1.1:20001" + {{- else }} + - "[fd00::1]:20001" + {{- end }} + - --listen-addr + {{- if .Values.networkValidator.listenAddr }} + - {{ .Values.networkValidator.listenAddr | quote }} + {{- else if .Values.disableIPv6}} + - "0.0.0.0:4140" + {{- else }} + - "[::]:4140" + {{- end }} + - --timeout + - {{ .Values.networkValidator.timeout }} + +{{- end -}} diff --git a/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_nodeselector.tpl b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_nodeselector.tpl new file mode 100644 index 000000000..4cde0ab16 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_nodeselector.tpl @@ -0,0 +1,4 @@ +{{- define "linkerd.node-selector" -}} +nodeSelector: +{{- toYaml .Values.nodeSelector | trim | nindent 2 }} +{{- end -}} diff --git a/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_proxy-config-ann.tpl b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_proxy-config-ann.tpl new file mode 100644 index 000000000..9651b3bd1 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_proxy-config-ann.tpl @@ -0,0 +1,18 @@ +{{- define "partials.proxy.config.annotations" -}} +{{- with .cpu }} +{{- with .request -}} +config.linkerd.io/proxy-cpu-request: {{. | quote}} +{{end}} +{{- with .limit -}} +config.linkerd.io/proxy-cpu-limit: {{. | quote}} +{{- end}} +{{- end}} +{{- with .memory }} +{{- with .request }} +config.linkerd.io/proxy-memory-request: {{. | quote}} +{{end}} +{{- with .limit -}} +config.linkerd.io/proxy-memory-limit: {{. | quote}} +{{- end}} +{{- end }} +{{- end }} diff --git a/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_proxy-init.tpl b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_proxy-init.tpl new file mode 100644 index 000000000..a307b1407 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_proxy-init.tpl @@ -0,0 +1,98 @@ +{{- define "partials.proxy-init" -}} +args: +{{- if (.Values.proxyInit.iptablesMode | default "legacy" | eq "nft") }} +- --firewall-bin-path +- "iptables-nft" +- --firewall-save-bin-path +- "iptables-nft-save" +{{- else if not (eq .Values.proxyInit.iptablesMode "legacy") }} +{{ fail (printf "Unsupported value \"%s\" for proxyInit.iptablesMode\nValid values: [\"nft\", \"legacy\"]" .Values.proxyInit.iptablesMode) }} +{{end -}} +{{- if .Values.disableIPv6 }} +- --ipv6=false +{{- end }} +- --incoming-proxy-port +- {{.Values.proxy.ports.inbound | quote}} +- --outgoing-proxy-port +- {{.Values.proxy.ports.outbound | quote}} +- --proxy-uid +- {{.Values.proxy.uid | quote}} +{{- if ge (int .Values.proxy.gid) 0 }} +- --proxy-gid +- {{.Values.proxy.gid | quote}} +{{- end }} +- --inbound-ports-to-ignore +- "{{.Values.proxy.ports.control}},{{.Values.proxy.ports.admin}}{{ternary (printf ",%s" (.Values.proxyInit.ignoreInboundPorts | toString)) "" (not (empty .Values.proxyInit.ignoreInboundPorts)) }}" +{{- if .Values.proxyInit.ignoreOutboundPorts }} +- --outbound-ports-to-ignore +- {{.Values.proxyInit.ignoreOutboundPorts | quote}} +{{- end }} +{{- if .Values.proxyInit.closeWaitTimeoutSecs }} +- --timeout-close-wait-secs +- {{ .Values.proxyInit.closeWaitTimeoutSecs | quote}} +{{- end }} +{{- if .Values.proxyInit.logFormat }} +- --log-format +- {{ .Values.proxyInit.logFormat }} +{{- end }} +{{- if .Values.proxyInit.logLevel }} +- --log-level +- {{ .Values.proxyInit.logLevel }} +{{- end }} +{{- if .Values.proxyInit.skipSubnets }} +- --subnets-to-ignore +- {{ .Values.proxyInit.skipSubnets | quote }} +{{- end }} +image: {{.Values.proxyInit.image.name}}:{{.Values.proxyInit.image.version}} +imagePullPolicy: {{.Values.proxyInit.image.pullPolicy | default .Values.imagePullPolicy}} +name: linkerd-init +{{ include "partials.resources" .Values.proxy.resources }} +securityContext: + {{- if or .Values.proxyInit.closeWaitTimeoutSecs .Values.proxyInit.privileged }} + allowPrivilegeEscalation: true + {{- else }} + allowPrivilegeEscalation: false + {{- end }} + capabilities: + add: + - NET_ADMIN + - NET_RAW + {{- if .Values.proxyInit.capabilities -}} + {{- if .Values.proxyInit.capabilities.add }} + {{- toYaml .Values.proxyInit.capabilities.add | trim | nindent 4 }} + {{- end }} + {{- if .Values.proxyInit.capabilities.drop -}} + {{- include "partials.proxy-init.capabilities.drop" . | nindent 4 -}} + {{- end }} + {{- end }} + {{- if or .Values.proxyInit.closeWaitTimeoutSecs .Values.proxyInit.privileged }} + privileged: true + {{- else }} + privileged: false + {{- end }} + {{- if .Values.proxyInit.runAsRoot }} + runAsGroup: 0 + runAsNonRoot: false + runAsUser: 0 + {{- else }} + runAsNonRoot: true + runAsUser: {{ .Values.proxyInit.runAsUser | int | eq 0 | ternary 65534 .Values.proxyInit.runAsUser }} + runAsGroup: {{ .Values.proxyInit.runAsGroup | int | eq 0 | ternary 65534 .Values.proxyInit.runAsGroup }} + {{- end }} + readOnlyRootFilesystem: true + seccompProfile: + type: RuntimeDefault +terminationMessagePolicy: FallbackToLogsOnError +{{- if or (not .Values.cniEnabled) .Values.proxyInit.saMountPath }} +volumeMounts: +{{- end -}} +{{- if not .Values.cniEnabled }} +- mountPath: {{.Values.proxyInit.xtMountPath.mountPath}} + name: {{.Values.proxyInit.xtMountPath.name}} +{{- end -}} +{{- if .Values.proxyInit.saMountPath }} +- mountPath: {{.Values.proxyInit.saMountPath.mountPath}} + name: {{.Values.proxyInit.saMountPath.name}} + readOnly: {{.Values.proxyInit.saMountPath.readOnly}} +{{- end -}} +{{- end -}} diff --git a/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_proxy.tpl b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_proxy.tpl new file mode 100644 index 000000000..4dcf12dee --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_proxy.tpl @@ -0,0 +1,271 @@ +{{ define "partials.proxy" -}} +{{ if and .Values.proxy.nativeSidecar .Values.proxy.waitBeforeExitSeconds }} +{{ fail "proxy.nativeSidecar and waitBeforeExitSeconds cannot be used simultaneously" }} +{{- end }} +{{- if not (has .Values.proxy.logHTTPHeaders (list "insecure" "off" "")) }} +{{- fail "logHTTPHeaders must be one of: insecure | off" }} +{{- end }} +{{- $trustDomain := (.Values.identityTrustDomain | default .Values.clusterDomain) -}} +env: +- name: _pod_name + valueFrom: + fieldRef: + fieldPath: metadata.name +- name: _pod_ns + valueFrom: + fieldRef: + fieldPath: metadata.namespace +- name: _pod_nodeName + valueFrom: + fieldRef: + fieldPath: spec.nodeName +{{- if .Values.proxy.cores }} +- name: LINKERD2_PROXY_CORES + value: {{.Values.proxy.cores | quote}} +{{- end }} +{{ if .Values.proxy.requireIdentityOnInboundPorts -}} +- name: LINKERD2_PROXY_INBOUND_PORTS_REQUIRE_IDENTITY + value: {{.Values.proxy.requireIdentityOnInboundPorts | quote}} +{{ end -}} +{{ if .Values.proxy.requireTLSOnInboundPorts -}} +- name: LINKERD2_PROXY_INBOUND_PORTS_REQUIRE_TLS + value: {{.Values.proxy.requireTLSOnInboundPorts | quote}} +{{ end -}} +- name: LINKERD2_PROXY_SHUTDOWN_ENDPOINT_ENABLED + value: {{.Values.proxy.enableShutdownEndpoint | quote}} +- name: LINKERD2_PROXY_LOG + value: "{{.Values.proxy.logLevel}}{{ if not (eq .Values.proxy.logHTTPHeaders "insecure") }},[{headers}]=off,[{request}]=off{{ end }}" +- name: LINKERD2_PROXY_LOG_FORMAT + value: {{.Values.proxy.logFormat | quote}} +- name: LINKERD2_PROXY_DESTINATION_SVC_ADDR + value: {{ternary "localhost.:8086" (printf "linkerd-dst-headless.%s.svc.%s.:8086" .Release.Namespace .Values.clusterDomain) (eq (toString .Values.proxy.component) "linkerd-destination")}} +- name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS + value: {{.Values.clusterNetworks | quote}} +- name: LINKERD2_PROXY_POLICY_SVC_ADDR + value: {{ternary "localhost.:8090" (printf "linkerd-policy.%s.svc.%s.:8090" .Release.Namespace .Values.clusterDomain) (eq (toString .Values.proxy.component) "linkerd-destination")}} +- name: LINKERD2_PROXY_POLICY_WORKLOAD + value: | + {"ns":"$(_pod_ns)", "pod":"$(_pod_name)"} +- name: LINKERD2_PROXY_INBOUND_DEFAULT_POLICY + value: {{.Values.proxy.defaultInboundPolicy}} +- name: LINKERD2_PROXY_POLICY_CLUSTER_NETWORKS + value: {{.Values.clusterNetworks | quote}} +- name: LINKERD2_PROXY_CONTROL_STREAM_INITIAL_TIMEOUT + value: {{((.Values.proxy.control).streams).initialTimeout | default "" | quote}} +- name: LINKERD2_PROXY_CONTROL_STREAM_IDLE_TIMEOUT + value: {{((.Values.proxy.control).streams).idleTimeout | default "" | quote}} +- name: LINKERD2_PROXY_CONTROL_STREAM_LIFETIME + value: {{((.Values.proxy.control).streams).lifetime | default "" | quote}} +{{ if .Values.proxy.inboundConnectTimeout -}} +- name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT + value: {{.Values.proxy.inboundConnectTimeout | quote}} +{{ end -}} +{{ if .Values.proxy.outboundConnectTimeout -}} +- name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT + value: {{.Values.proxy.outboundConnectTimeout | quote}} +{{ end -}} +{{ if .Values.proxy.outboundDiscoveryCacheUnusedTimeout -}} +- name: LINKERD2_PROXY_OUTBOUND_DISCOVERY_IDLE_TIMEOUT + value: {{.Values.proxy.outboundDiscoveryCacheUnusedTimeout | quote}} +{{ end -}} +{{ if .Values.proxy.inboundDiscoveryCacheUnusedTimeout -}} +- name: LINKERD2_PROXY_INBOUND_DISCOVERY_IDLE_TIMEOUT + value: {{.Values.proxy.inboundDiscoveryCacheUnusedTimeout | quote}} +{{ end -}} +{{ if .Values.proxy.disableOutboundProtocolDetectTimeout -}} +- name: LINKERD2_PROXY_OUTBOUND_DETECT_TIMEOUT + value: "365d" +{{ end -}} +{{ if .Values.proxy.disableInboundProtocolDetectTimeout -}} +- name: LINKERD2_PROXY_INBOUND_DETECT_TIMEOUT + value: "365d" +{{ end -}} +- name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR + value: "{{ if .Values.disableIPv6 }}0.0.0.0{{ else }}[::]{{ end }}:{{.Values.proxy.ports.control}}" +- name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR + value: "{{ if .Values.disableIPv6 }}0.0.0.0{{ else }}[::]{{ end }}:{{.Values.proxy.ports.admin}}" +{{- /* Deprecated, superseded by LINKERD2_PROXY_OUTBOUND_LISTEN_ADDRS since proxy's v2.228.0 (deployed since edge-24.4.5) */}} +- name: LINKERD2_PROXY_OUTBOUND_LISTEN_ADDR + value: "127.0.0.1:{{.Values.proxy.ports.outbound}}" +- name: LINKERD2_PROXY_OUTBOUND_LISTEN_ADDRS + value: "127.0.0.1:{{.Values.proxy.ports.outbound}}{{ if not .Values.disableIPv6}},[::1]:{{.Values.proxy.ports.outbound}}{{ end }}" +- name: LINKERD2_PROXY_INBOUND_LISTEN_ADDR + value: "{{ if .Values.disableIPv6 }}0.0.0.0{{ else }}[::]{{ end }}:{{.Values.proxy.ports.inbound}}" +- name: LINKERD2_PROXY_INBOUND_IPS + valueFrom: + fieldRef: + fieldPath: status.podIPs +- name: LINKERD2_PROXY_INBOUND_PORTS + value: {{ .Values.proxy.podInboundPorts | quote }} +{{ if .Values.proxy.isGateway -}} +- name: LINKERD2_PROXY_INBOUND_GATEWAY_SUFFIXES + value: {{printf "svc.%s." .Values.clusterDomain}} +{{ end -}} +{{ if .Values.proxy.isIngress -}} +- name: LINKERD2_PROXY_INGRESS_MODE + value: "true" +{{ end -}} +- name: LINKERD2_PROXY_DESTINATION_PROFILE_SUFFIXES + {{- $internalDomain := printf "svc.%s." .Values.clusterDomain }} + value: {{ternary "." $internalDomain .Values.proxy.enableExternalProfiles}} +- name: LINKERD2_PROXY_INBOUND_ACCEPT_KEEPALIVE + value: 10000ms +- name: LINKERD2_PROXY_OUTBOUND_CONNECT_KEEPALIVE + value: 10000ms +- name: LINKERD2_PROXY_INBOUND_ACCEPT_USER_TIMEOUT + value: 30s +- name: LINKERD2_PROXY_OUTBOUND_CONNECT_USER_TIMEOUT + value: 30s +{{- /* Configure inbound and outbound parameters, e.g. for HTTP/2 servers. */}} +{{ range $proxyK, $proxyV := (dict "inbound" .Values.proxy.inbound "outbound" .Values.proxy.outbound) -}} +{{ range $scopeK, $scopeV := $proxyV -}} +{{ range $protoK, $protoV := $scopeV -}} +{{ range $paramK, $paramV := $protoV -}} +- name: LINKERD2_PROXY_{{snakecase $proxyK | upper}}_{{snakecase $scopeK | upper}}_{{snakecase $protoK | upper}}_{{snakecase $paramK | upper}} + value: {{ quote $paramV }} +{{ end -}} +{{ end -}} +{{ end -}} +{{ end -}} +{{ if .Values.proxy.opaquePorts -}} +- name: LINKERD2_PROXY_INBOUND_PORTS_DISABLE_PROTOCOL_DETECTION + value: {{.Values.proxy.opaquePorts | quote}} +{{ end -}} +- name: LINKERD2_PROXY_DESTINATION_CONTEXT + value: | + {"ns":"$(_pod_ns)", "nodeName":"$(_pod_nodeName)", "pod":"$(_pod_name)"} +- name: _pod_sa + valueFrom: + fieldRef: + fieldPath: spec.serviceAccountName +- name: _l5d_ns + value: {{.Release.Namespace}} +- name: _l5d_trustdomain + value: {{$trustDomain}} +- name: LINKERD2_PROXY_IDENTITY_DIR + value: /var/run/linkerd/identity/end-entity +- name: LINKERD2_PROXY_IDENTITY_TRUST_ANCHORS +{{- /* +Pods in the `linkerd` namespace are not injected by the proxy injector and instead obtain +the trust anchor bundle from the `linkerd-identity-trust-roots` configmap. This should not +be used in other contexts. +*/}} +{{- if .Values.proxy.loadTrustBundleFromConfigMap }} + valueFrom: + configMapKeyRef: + name: linkerd-identity-trust-roots + key: ca-bundle.crt +{{ else }} + value: | + {{- required "Please provide the identity trust anchors" .Values.identityTrustAnchorsPEM | trim | nindent 4 }} +{{ end -}} +- name: LINKERD2_PROXY_IDENTITY_TOKEN_FILE +{{- if .Values.identity.serviceAccountTokenProjection }} + value: /var/run/secrets/tokens/linkerd-identity-token +{{ else }} + value: /var/run/secrets/kubernetes.io/serviceaccount/token +{{ end -}} +- name: LINKERD2_PROXY_IDENTITY_SVC_ADDR + value: {{ternary "localhost.:8080" (printf "linkerd-identity-headless.%s.svc.%s.:8080" .Release.Namespace .Values.clusterDomain) (eq (toString .Values.proxy.component) "linkerd-identity")}} +- name: LINKERD2_PROXY_IDENTITY_LOCAL_NAME + value: $(_pod_sa).$(_pod_ns).serviceaccount.identity.{{.Release.Namespace}}.{{$trustDomain}} +- name: LINKERD2_PROXY_IDENTITY_SVC_NAME + value: linkerd-identity.{{.Release.Namespace}}.serviceaccount.identity.{{.Release.Namespace}}.{{$trustDomain}} +- name: LINKERD2_PROXY_DESTINATION_SVC_NAME + value: linkerd-destination.{{.Release.Namespace}}.serviceaccount.identity.{{.Release.Namespace}}.{{$trustDomain}} +- name: LINKERD2_PROXY_POLICY_SVC_NAME + value: linkerd-destination.{{.Release.Namespace}}.serviceaccount.identity.{{.Release.Namespace}}.{{$trustDomain}} +{{ if .Values.proxy.accessLog -}} +- name: LINKERD2_PROXY_ACCESS_LOG + value: {{.Values.proxy.accessLog | quote}} +{{ end -}} +{{ if .Values.proxy.shutdownGracePeriod -}} +- name: LINKERD2_PROXY_SHUTDOWN_GRACE_PERIOD + value: {{.Values.proxy.shutdownGracePeriod | quote}} +{{ end -}} +{{ if .Values.proxy.additionalEnv -}} +{{ toYaml .Values.proxy.additionalEnv }} +{{ end -}} +{{ if .Values.proxy.experimentalEnv -}} +{{ toYaml .Values.proxy.experimentalEnv }} +{{ end -}} +image: {{.Values.proxy.image.name}}:{{.Values.proxy.image.version | default .Values.linkerdVersion}} +imagePullPolicy: {{.Values.proxy.image.pullPolicy | default .Values.imagePullPolicy}} +livenessProbe: + httpGet: + path: /live + port: {{.Values.proxy.ports.admin}} + initialDelaySeconds: {{.Values.proxy.livenessProbe.initialDelaySeconds }} + timeoutSeconds: {{.Values.proxy.livenessProbe.timeoutSeconds }} +name: linkerd-proxy +ports: +- containerPort: {{.Values.proxy.ports.inbound}} + name: linkerd-proxy +- containerPort: {{.Values.proxy.ports.admin}} + name: linkerd-admin +readinessProbe: + httpGet: + path: /ready + port: {{.Values.proxy.ports.admin}} + initialDelaySeconds: {{.Values.proxy.readinessProbe.initialDelaySeconds }} + timeoutSeconds: {{.Values.proxy.readinessProbe.timeoutSeconds }} +{{- if and .Values.proxy.nativeSidecar .Values.proxy.await }} +startupProbe: + httpGet: + path: /ready + port: {{.Values.proxy.ports.admin}} + initialDelaySeconds: {{.Values.proxy.startupProbe.initialDelaySeconds}} + periodSeconds: {{.Values.proxy.startupProbe.periodSeconds}} + failureThreshold: {{.Values.proxy.startupProbe.failureThreshold}} +{{- end }} +{{- if .Values.proxy.resources }} +{{ include "partials.resources" .Values.proxy.resources }} +{{- end }} +securityContext: + allowPrivilegeEscalation: false + {{- if .Values.proxy.capabilities -}} + {{- include "partials.proxy.capabilities" . | nindent 2 -}} + {{- end }} + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: {{.Values.proxy.uid}} +{{- if ge (int .Values.proxy.gid) 0 }} + runAsGroup: {{.Values.proxy.gid}} +{{- end }} + seccompProfile: + type: RuntimeDefault +terminationMessagePolicy: FallbackToLogsOnError +{{- if and (not .Values.proxy.nativeSidecar) (or .Values.proxy.await .Values.proxy.waitBeforeExitSeconds) }} +lifecycle: +{{- if .Values.proxy.await }} + postStart: + exec: + command: + - /usr/lib/linkerd/linkerd-await + - --timeout=2m + - --port={{.Values.proxy.ports.admin}} +{{- end }} +{{- if .Values.proxy.waitBeforeExitSeconds }} + preStop: + exec: + command: + - /bin/sleep + - {{.Values.proxy.waitBeforeExitSeconds | quote}} +{{- end }} +{{- end }} +volumeMounts: +- mountPath: /var/run/linkerd/identity/end-entity + name: linkerd-identity-end-entity +{{- if .Values.identity.serviceAccountTokenProjection }} +- mountPath: /var/run/secrets/tokens + name: linkerd-identity-token +{{- end }} +{{- if .Values.proxy.saMountPath }} +- mountPath: {{.Values.proxy.saMountPath.mountPath}} + name: {{.Values.proxy.saMountPath.name}} + readOnly: {{.Values.proxy.saMountPath.readOnly}} +{{- end -}} +{{- if .Values.proxy.nativeSidecar }} +restartPolicy: Always +{{- end -}} +{{- end }} diff --git a/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_pull-secrets.tpl b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_pull-secrets.tpl new file mode 100644 index 000000000..0c9aa4f01 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_pull-secrets.tpl @@ -0,0 +1,6 @@ +{{- define "partials.image-pull-secrets"}} +{{- if . }} +imagePullSecrets: +{{ toYaml . | indent 2 }} +{{- end }} +{{- end -}} diff --git a/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_resources.tpl b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_resources.tpl new file mode 100644 index 000000000..1fd6789fd --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_resources.tpl @@ -0,0 +1,28 @@ +{{- define "partials.resources" -}} +{{- $ephemeralStorage := index . "ephemeral-storage" -}} +resources: + {{- if or (.cpu).limit (.memory).limit ($ephemeralStorage).limit }} + limits: + {{- with (.cpu).limit }} + cpu: {{. | quote}} + {{- end }} + {{- with (.memory).limit }} + memory: {{. | quote}} + {{- end }} + {{- with ($ephemeralStorage).limit }} + ephemeral-storage: {{. | quote}} + {{- end }} + {{- end }} + {{- if or (.cpu).request (.memory).request ($ephemeralStorage).request }} + requests: + {{- with (.cpu).request }} + cpu: {{. | quote}} + {{- end }} + {{- with (.memory).request }} + memory: {{. | quote}} + {{- end }} + {{- with ($ephemeralStorage).request }} + ephemeral-storage: {{. | quote}} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_tolerations.tpl b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_tolerations.tpl new file mode 100644 index 000000000..c2292b146 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_tolerations.tpl @@ -0,0 +1,4 @@ +{{- define "linkerd.tolerations" -}} +tolerations: +{{ toYaml .Values.tolerations | trim | indent 2 }} +{{- end -}} diff --git a/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_trace.tpl b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_trace.tpl new file mode 100644 index 000000000..dee059541 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_trace.tpl @@ -0,0 +1,5 @@ +{{ define "partials.linkerd.trace" -}} +{{ if .Values.controlPlaneTracing -}} +- -trace-collector=collector.{{.Values.controlPlaneTracingNamespace}}.svc.{{.Values.clusterDomain}}:55678 +{{ end -}} +{{- end }} diff --git a/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_validate.tpl b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_validate.tpl new file mode 100644 index 000000000..ba772c2fe --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_validate.tpl @@ -0,0 +1,19 @@ +{{- define "linkerd.webhook.validation" -}} + +{{- if and (.injectCaFrom) (.injectCaFromSecret) -}} +{{- fail "injectCaFrom and injectCaFromSecret cannot both be set" -}} +{{- end -}} + +{{- if and (or (.injectCaFrom) (.injectCaFromSecret)) (.caBundle) -}} +{{- fail "injectCaFrom or injectCaFromSecret cannot be set if providing a caBundle" -}} +{{- end -}} + +{{- if and (.externalSecret) (empty .caBundle) (empty .injectCaFrom) (empty .injectCaFromSecret) -}} +{{- fail "if externalSecret is set, then caBundle, injectCaFrom, or injectCaFromSecret must be set" -}} +{{- end }} + +{{- if and (or .injectCaFrom .injectCaFromSecret .caBundle) (not .externalSecret) -}} +{{- fail "if caBundle, injectCaFrom, or injectCaFromSecret is set, then externalSecret must be set" -}} +{{- end -}} + +{{- end -}} diff --git a/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_volumes.tpl b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_volumes.tpl new file mode 100644 index 000000000..ecb24cfe6 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/templates/_volumes.tpl @@ -0,0 +1,41 @@ +{{ define "partials.proxy.volumes.identity" -}} +emptyDir: + medium: Memory +name: linkerd-identity-end-entity +{{- end -}} + +{{ define "partials.proxyInit.volumes.xtables" -}} +emptyDir: {} +name: {{ .Values.proxyInit.xtMountPath.name }} +{{- end -}} + +{{- define "partials.proxy.volumes.service-account-token" -}} +name: linkerd-identity-token +projected: + sources: + - serviceAccountToken: + path: linkerd-identity-token + expirationSeconds: 86400 {{- /* # 24 hours */}} + audience: identity.l5d.io +{{- end -}} + +{{- define "partials.volumes.manual-mount-service-account-token" -}} +name: kube-api-access +projected: + defaultMode: 420 + sources: + - serviceAccountToken: + expirationSeconds: 3607 + path: token + - configMap: + items: + - key: ca.crt + path: ca.crt + name: kube-root-ca.crt + - downwardAPI: + items: + - fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + path: namespace +{{- end -}} \ No newline at end of file diff --git a/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/values.yaml b/charts/buoyant/linkerd-crds/2024.10.4/charts/partials/values.yaml new file mode 100644 index 000000000..e69de29bb diff --git a/charts/buoyant/linkerd-crds/2024.10.4/templates/NOTES.txt b/charts/buoyant/linkerd-crds/2024.10.4/templates/NOTES.txt new file mode 100644 index 000000000..4ff5c1818 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/templates/NOTES.txt @@ -0,0 +1,6 @@ +The linkerd-crds chart was successfully installed 🎉 + +To complete the linkerd core installation, please now proceed to install the +linkerd-control-plane chart in the {{ .Release.Namespace }} namespace. + +Looking for more? Visit https://linkerd.io/2/getting-started/ diff --git a/charts/buoyant/linkerd-crds/2024.10.4/templates/gateway.networking.k8s.io_grpcroutes.yaml b/charts/buoyant/linkerd-crds/2024.10.4/templates/gateway.networking.k8s.io_grpcroutes.yaml new file mode 100644 index 000000000..0050aac88 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/templates/gateway.networking.k8s.io_grpcroutes.yaml @@ -0,0 +1,1507 @@ +{{- if .Values.enableHttpRoutes }} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1923 + gateway.networking.k8s.io/bundle-version: v0.7.1 + gateway.networking.k8s.io/channel: experimental + {{ include "partials.annotations.created-by" . }} + labels: + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + linkerd.io/control-plane-ns: {{.Release.Namespace}} + 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: 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 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. \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 the Kubernetes resource + kind of the referent. For example \"Service\". + \n Defaults to \"Service\" when not specified. + \n 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. \n Support: Core (Services with + a type other than ExternalName) \n 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. \n 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. \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" + 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 the Kubernetes resource kind of + the referent. For example \"Service\". \n Defaults to + \"Service\" when not specified. \n 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. + \n Support: Core (Services with a type other than ExternalName) + \n 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. \n + 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. \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 the Kubernetes resource + kind of the referent. For example \"Service\". + \n Defaults to \"Service\" when not specified. + \n 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. \n Support: Core (Services with a + type other than ExternalName) \n 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. \n 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. \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" + 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: + 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: + 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." + maxLength: 1024 + 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." + 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) \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 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 + }" + 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: null + storedVersions: null +{{- end }} + diff --git a/charts/buoyant/linkerd-crds/2024.10.4/templates/gateway.networking.k8s.io_httproutes.yaml b/charts/buoyant/linkerd-crds/2024.10.4/templates/gateway.networking.k8s.io_httproutes.yaml new file mode 100644 index 000000000..b695c51d5 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/templates/gateway.networking.k8s.io_httproutes.yaml @@ -0,0 +1,3881 @@ +{{- if .Values.enableHttpRoutes }} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1923 + gateway.networking.k8s.io/bundle-version: v0.7.1 + gateway.networking.k8s.io/channel: experimental + {{ include "partials.annotations.created-by" . }} + labels: + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + linkerd.io/control-plane-ns: {{.Release.Namespace}} + 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 + 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 used to process + the request. Implementations MUST ignore any port value specified + in the HTTP Host header while performing a match. \n Valid values + for Hostnames are determined by 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: Extended + for Kubernetes ServiceImport \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 the Kubernetes resource + kind of the referent. For example \"Service\". + \n Defaults to \"Service\" when not specified. + \n 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. \n Support: Core (Services with + a type other than ExternalName) \n 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. \n 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. \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 + in the `Host` header 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" + 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\" 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." + 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`." + 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. \n If no port is specified, the + redirect port MUST be derived using the following + rules: \n * 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. \n Implementations SHOULD NOT + add the port number in the 'Location' header + in the following cases: \n * 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. \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 Scheme redirects can affect the + port of the redirect, for more information, + refer to the documentation for the port field + of this filter. \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" + 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`." + 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" + properties: + hostname: + description: "Hostname is the value to be used + to replace the Host header value during forwarding. + \n 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. \n + 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\" 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." + 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`." + 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 the Kubernetes resource kind of + the referent. For example \"Service\". \n Defaults to + \"Service\" when not specified. \n 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. + \n Support: Core (Services with a type other than ExternalName) + \n 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. \n + 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. \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 the Kubernetes resource + kind of the referent. For example \"Service\". + \n Defaults to \"Service\" when not specified. + \n 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. \n Support: Core (Services with a + type other than ExternalName) \n 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. \n 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. \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 in the `Host` header 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" + 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\" 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." + 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`." + 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. \n If + no port is specified, the redirect port MUST be + derived using the following rules: \n * 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. + \n Implementations SHOULD NOT add the port number + in the 'Location' header in the following cases: + \n * 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. \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 + Scheme redirects can affect the port of the redirect, + for more information, refer to the documentation + for the port field of this filter. \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" + 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`." + 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" + properties: + hostname: + description: "Hostname is the value to be used to + replace the Host header value during forwarding. + \n 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. \n 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\" 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." + 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`." + 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 having: + \n * \"Exact\" path match. * \"Prefix\" path match with largest + number of characters. * Method match. * Largest number of + header matches. * Largest number of query param matches. \n + Note: The precedence of RegularExpression path matches are + implementation-specific. \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 path: value: \"/foo\" headers: - name: \"version\" + 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 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + 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 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 + }" + 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 used to process + the request. Implementations MUST ignore any port value specified + in the HTTP Host header while performing a match. \n Valid values + for Hostnames are determined by 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: Extended + for Kubernetes ServiceImport \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 the Kubernetes resource + kind of the referent. For example \"Service\". + \n Defaults to \"Service\" when not specified. + \n 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. \n Support: Core (Services with + a type other than ExternalName) \n 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. \n 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. \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 + in the `Host` header 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" + 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\" 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." + 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`." + 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. \n If no port is specified, the + redirect port MUST be derived using the following + rules: \n * 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. \n Implementations SHOULD NOT + add the port number in the 'Location' header + in the following cases: \n * 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. \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 Scheme redirects can affect the + port of the redirect, for more information, + refer to the documentation for the port field + of this filter. \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" + 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`." + 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" + properties: + hostname: + description: "Hostname is the value to be used + to replace the Host header value during forwarding. + \n 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. \n + 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\" 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." + 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`." + 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 the Kubernetes resource kind of + the referent. For example \"Service\". \n Defaults to + \"Service\" when not specified. \n 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. + \n Support: Core (Services with a type other than ExternalName) + \n 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. \n + 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. \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 the Kubernetes resource + kind of the referent. For example \"Service\". + \n Defaults to \"Service\" when not specified. + \n 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. \n Support: Core (Services with a + type other than ExternalName) \n 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. \n 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. \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 in the `Host` header 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" + 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\" 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." + 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`." + 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. \n If + no port is specified, the redirect port MUST be + derived using the following rules: \n * 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. + \n Implementations SHOULD NOT add the port number + in the 'Location' header in the following cases: + \n * 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. \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 + Scheme redirects can affect the port of the redirect, + for more information, refer to the documentation + for the port field of this filter. \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" + 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`." + 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" + properties: + hostname: + description: "Hostname is the value to be used to + replace the Host header value during forwarding. + \n 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. \n 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\" 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." + 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`." + 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 having: + \n * \"Exact\" path match. * \"Prefix\" path match with largest + number of characters. * Method match. * Largest number of + header matches. * Largest number of query param matches. \n + Note: The precedence of RegularExpression path matches are + implementation-specific. \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 path: value: \"/foo\" headers: - name: \"version\" + 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 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + 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 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 + }" + 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: null + storedVersions: null +{{- end }} + diff --git a/charts/buoyant/linkerd-crds/2024.10.4/templates/gateway.networking.k8s.io_tcproutes.yaml b/charts/buoyant/linkerd-crds/2024.10.4/templates/gateway.networking.k8s.io_tcproutes.yaml new file mode 100644 index 000000000..cb17293b7 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/templates/gateway.networking.k8s.io_tcproutes.yaml @@ -0,0 +1,533 @@ +{{- if .Values.enableTcpRoutes }} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1923 + gateway.networking.k8s.io/bundle-version: v0.7.1 + gateway.networking.k8s.io/channel: experimental + {{ include "partials.annotations.created-by" . }} + labels: + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + linkerd.io/control-plane-ns: {{.Release.Namespace}} + creationTimestamp: null + 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: Extended + for Kubernetes ServiceImport \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 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." + 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\". \n Defaults to + \"Service\" when not specified. \n 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. + \n Support: Core (Services with a type other than ExternalName) + \n 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. \n + 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. \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 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 + }" + 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: null + storedVersions: null +{{- end }} diff --git a/charts/buoyant/linkerd-crds/2024.10.4/templates/gateway.networking.k8s.io_tlsroutes.yaml b/charts/buoyant/linkerd-crds/2024.10.4/templates/gateway.networking.k8s.io_tlsroutes.yaml new file mode 100644 index 000000000..d79a19aae --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/templates/gateway.networking.k8s.io_tlsroutes.yaml @@ -0,0 +1,582 @@ +{{- if .Values.enableTlsRoutes }} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1923 + gateway.networking.k8s.io/bundle-version: v0.7.1 + gateway.networking.k8s.io/channel: experimental + {{ include "partials.annotations.created-by" . }} + labels: + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + linkerd.io/control-plane-ns: {{.Release.Namespace}} + creationTimestamp: null + 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: Extended for Kubernetes ServiceImport + \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 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." + 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\". \n Defaults to + \"Service\" when not specified. \n 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. + \n Support: Core (Services with a type other than ExternalName) + \n 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. \n + 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. \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 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 + }" + 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: null + storedVersions: null +{{- end }} diff --git a/charts/buoyant/linkerd-crds/2024.10.4/templates/policy/authorization-policy.yaml b/charts/buoyant/linkerd-crds/2024.10.4/templates/policy/authorization-policy.yaml new file mode 100644 index 000000000..7d86520e2 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/templates/policy/authorization-policy.yaml @@ -0,0 +1,99 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: authorizationpolicies.policy.linkerd.io + annotations: + {{ include "partials.annotations.created-by" . }} + labels: + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + linkerd.io/control-plane-ns: {{.Release.Namespace}} +spec: + group: policy.linkerd.io + scope: Namespaced + names: + kind: AuthorizationPolicy + plural: authorizationpolicies + singular: authorizationpolicy + shortNames: [authzpolicy] + versions: + - name: v1alpha1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + required: [spec] + properties: + spec: + description: >- + Authorizes clients to communicate with Linkerd-proxied server + resources. + type: object + required: [targetRef, requiredAuthenticationRefs] + properties: + targetRef: + description: >- + TargetRef references a resource to which the authorization + policy applies. + type: object + required: [kind, name] + # Modified from the gateway API. + # Copyright 2020 The Kubernetes Authors + properties: + group: + description: >- + Group is the group of the referent. When empty, the + Kubernetes 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 the 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 + requiredAuthenticationRefs: + description: >- + RequiredAuthenticationRefs enumerates a set of required + authentications. ALL authentications must be satisfied for + the authorization to apply. If any of the referred objects + cannot be found, the authorization will be ignored. + type: array + items: + type: object + required: [kind, name] + properties: + group: + description: >- + Group is the group of the referent. When empty, the + Kubernetes 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 the 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: >- + Name is the name of the referent. When unspecified, + this authentication refers to the local namespace. + maxLength: 253 + type: string diff --git a/charts/buoyant/linkerd-crds/2024.10.4/templates/policy/egress-network.yaml b/charts/buoyant/linkerd-crds/2024.10.4/templates/policy/egress-network.yaml new file mode 100644 index 000000000..4289de057 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/templates/policy/egress-network.yaml @@ -0,0 +1,123 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: egressnetworks.policy.linkerd.io + annotations: + {{ include "partials.annotations.created-by" . }} + labels: + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + linkerd.io/control-plane-ns: {{.Release.Namespace}} +spec: + group: policy.linkerd.io + names: + categories: + - policy + kind: EgressNetwork + listKind: EgressNetworkList + plural: egressnetworks + singular: egressnetwork + scope: Namespaced + versions: + - name: v1alpha1 + served: true + storage: true + subresources: + status: {} + schema: + openAPIV3Schema: + description: >- + An EgressNetwork captures traffic to egress destinations + type: object + required: [spec] + properties: + apiVerson: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + trafficPolicy: + description: >- + This field controls the traffic policy enforced upon traffic + that does not match any explicit route resources associated + with an instance of this object. The values that are allowed + currently are: + - Allow - permits all traffic, even if it has not been + explicitly described via attaching an xRoute + resources. + - Deny - blocks all traffic that has not been described via + attaching an xRoute resource. + type: string + enum: + - Allow + - Deny + networks: + type: array + items: + type: object + required: [cidr] + properties: + cidr: + description: >- + The CIDR of the network to be authorized. + type: string + except: + description: >- + A list of IP networks/addresses not to be included in + the above `cidr`. + type: array + items: + type: string + type: object + required: + - trafficPolicy + status: + type: object + properties: + conditions: + type: array + items: + type: object + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the + condition transitioned from one status to another. + format: date-time + type: string + status: + description: status of the condition (one of True, False, Unknown) + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of the condition in CamelCase or in + foo.example.com/CamelCase. + 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 + 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 + message: + description: message is a human readable message + indicating details about the transition. This may be an + empty string. + maxLength: 32768 + type: string + required: + - status + - type diff --git a/charts/buoyant/linkerd-crds/2024.10.4/templates/policy/httproute.yaml b/charts/buoyant/linkerd-crds/2024.10.4/templates/policy/httproute.yaml new file mode 100644 index 000000000..6d2e8b07e --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/templates/policy/httproute.yaml @@ -0,0 +1,5328 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: httproutes.policy.linkerd.io + annotations: + {{ include "partials.annotations.created-by" . }} + labels: + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + linkerd.io/control-plane-ns: {{.Release.Namespace}} +spec: + group: policy.linkerd.io + names: + 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: v1alpha1 + 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 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." + 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: policy.linkerd.io + description: "Group is the group of the referent. \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) Support: Custom (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 (or empty string), this refers to the local namespace + of the Route. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "port" + 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) and processing it (filters). + properties: + backendRefs: + type: array + items: + type: object + properties: + name: + type: string + port: + type: integer + namespace: + type: string + default: "default" + 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: + 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 + 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 + in the `Host` header 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" + 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\". \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 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. \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`." + 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. \n If no port is specified, the + redirect port MUST be derived using the following + rules: \n * 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. \n Implementations SHOULD NOT + add the port number in the 'Location' header + in the following cases: \n * 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. \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 Scheme redirects can affect the + port of the redirect, for more information, + refer to the documentation for the port field + of this filter. \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" + 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`." + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestRedirect + type: string + required: + - type + 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 custom 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: + 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\" \n Output: GET /foo HTTP/1.1 my-header: + foo 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 + 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 + 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" + 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\". \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 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. \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`." + 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 + Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status code to + be used in response. \n Support: Core" + enum: + - 301 + - 302 + type: integer + 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\n " + enum: + - RequestHeaderModifier + - RequestRedirect + type: string + 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 rules based on the + following criteria, continuing on ties. Precedence must be + given to the the Rule with the largest number of: \n * Characters + in a matching non-wildcard hostname. * Characters in a matching + hostname. * 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 the Route that has been given precedence, + matching precedence MUST be granted to the first matching + rule 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: path: value: \"/foo\" headers: - name: + \"version\" value \"v1\" ```" + 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: Custom (RegularExpression) \n Since + RegularExpression HeaderMatchType has custom 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: Custom (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. + 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). + 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: Custom (RegularExpression) + \n Since RegularExpression QueryParamMatchType + has custom 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 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, 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 }" + 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: policy.linkerd.io + description: "Group is the group of the referent. \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) Support: Custom (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 (or empty string), this refers to the + local namespace of the Route. \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 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." + 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: policy.linkerd.io + description: "Group is the group of the referent. \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) Support: Custom (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 (or empty string), this refers to the local namespace + of the Route. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "port" + 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) and processing it (filters). + properties: + backendRefs: + type: array + items: + type: object + properties: + name: + type: string + port: + type: integer + namespace: + type: string + default: "default" + 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: + 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 + 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 + in the `Host` header 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" + 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\". \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 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. \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`." + 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. \n If no port is specified, the + redirect port MUST be derived using the following + rules: \n * 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. \n Implementations SHOULD NOT + add the port number in the 'Location' header + in the following cases: \n * 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. \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 Scheme redirects can affect the + port of the redirect, for more information, + refer to the documentation for the port field + of this filter. \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" + 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`." + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestRedirect + type: string + required: + - type + 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 custom 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: + 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\" \n Output: GET /foo HTTP/1.1 my-header: + foo 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 + 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 + 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" + 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\". \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 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. \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`." + 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 + Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status code to + be used in response. \n Support: Core" + enum: + - 301 + - 302 + type: integer + 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\"." + enum: + - RequestHeaderModifier + - RequestRedirect + type: string + 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 rules based on the + following criteria, continuing on ties. Precedence must be + given to the the Rule with the largest number of: \n * Characters + in a matching non-wildcard hostname. * Characters in a matching + hostname. * 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 the Route that has been given precedence, + matching precedence MUST be granted to the first matching + rule 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: path: value: \"/foo\" headers: - name: + \"version\" value \"v1\" ```" + 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: Custom (RegularExpression) \n Since + RegularExpression HeaderMatchType has custom 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: Custom (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. + 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). + 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: Custom (RegularExpression) + \n Since RegularExpression QueryParamMatchType + has custom 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 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, 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 }" + 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: policy.linkerd.io + description: "Group is the group of the referent. \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) Support: Custom (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 (or empty string), this refers to the + local namespace of the Route. \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: v1beta2 + 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 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." + 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: policy.linkerd.io + description: "Group is the group of the referent. \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) Support: Custom (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 (or empty string), this refers to the local namespace + of the Route. \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. \n 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: \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) and processing it (filters). + 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: + 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 + 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: + 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 + 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 + in the `Host` header 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" + 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\". \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 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. \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`." + 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. \n If no port is specified, the + redirect port MUST be derived using the following + rules: \n * 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. \n Implementations SHOULD NOT + add the port number in the 'Location' header + in the following cases: \n * 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. \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 Scheme redirects can affect the + port of the redirect, for more information, + refer to the documentation for the port field + of this filter. \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" + 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`." + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestRedirect + type: string + required: + - type + type: object + maxItems: 16 + type: array + 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 custom 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: + 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\" \n Output: GET /foo HTTP/1.1 my-header: + foo 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 + 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 + 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" + 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\". \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 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. \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`." + 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 + Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status code to + be used in response. \n Support: Core" + enum: + - 301 + - 302 + type: integer + 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\"." + enum: + - RequestHeaderModifier + - RequestRedirect + type: string + 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 rules based on the + following criteria, continuing on ties. Precedence must be + given to the the Rule with the largest number of: \n * Characters + in a matching non-wildcard hostname. * Characters in a matching + hostname. * 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 the Route that has been given precedence, + matching precedence MUST be granted to the first matching + rule 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: path: value: \"/foo\" headers: - name: + \"version\" value \"v1\" ```" + 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: Custom (RegularExpression) \n Since + RegularExpression HeaderMatchType has custom 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: Custom (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. + 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). + 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: Custom (RegularExpression) + \n Since RegularExpression QueryParamMatchType + has custom 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 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, 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 }" + 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: policy.linkerd.io + description: "Group is the group of the referent. \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) Support: Custom (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 (or empty string), this refers to the + local namespace of the Route. \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: v1beta3 + 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 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." + 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: policy.linkerd.io + description: "Group is the group of the referent. \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) Support: Custom (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 (or empty string), this refers to the local namespace + of the Route. \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. \n 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: \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) and processing it (filters). + 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: + 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 + 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: + 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 + 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 + in the `Host` header 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" + 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\". \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 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. \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`." + 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. \n If no port is specified, the + redirect port MUST be derived using the following + rules: \n * 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. \n Implementations SHOULD NOT + add the port number in the 'Location' header + in the following cases: \n * 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. \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 Scheme redirects can affect the + port of the redirect, for more information, + refer to the documentation for the port field + of this filter. \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" + 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`." + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestRedirect + type: string + required: + - type + type: object + maxItems: 16 + type: array + 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 custom 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: + 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\" \n Output: GET /foo HTTP/1.1 my-header: + foo 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 + 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 + 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" + 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\". \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 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. \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`." + 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 + Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status code to + be used in response. \n Support: Core" + enum: + - 301 + - 302 + type: integer + 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\"." + enum: + - RequestHeaderModifier + - RequestRedirect + type: string + 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 rules based on the + following criteria, continuing on ties. Precedence must be + given to the the Rule with the largest number of: \n * Characters + in a matching non-wildcard hostname. * Characters in a matching + hostname. * 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 the Route that has been given precedence, + matching precedence MUST be granted to the first matching + rule 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: path: value: \"/foo\" headers: - name: + \"version\" value \"v1\" ```" + 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: Custom (RegularExpression) \n Since + RegularExpression HeaderMatchType has custom 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: Custom (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. + 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). + 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: Custom (RegularExpression) + \n Since RegularExpression QueryParamMatchType + has custom 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 + timeouts: + description: "Timeouts defines the timeouts that can be configured + for an HTTP request. \n Support: Core \n " + properties: + backendRequest: + description: "BackendRequest specifies a timeout for an + individual request from the gateway to a backend service. + Typically used in conjunction with automatic retries, + if supported by an implementation. Default is the value + of Request timeout. \n Support: Extended" + format: duration + type: string + request: + description: "Request specifies a timeout for responding + to client HTTP requests, disabled by default. \n For example, + the following rule will timeout if a client request is + taking longer than 10 seconds to complete: \n ``` rules: + - timeouts: request: 10s backendRefs: ... ``` \n Support: + Core" + format: duration + type: string + type: object + 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 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, 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 }" + 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: policy.linkerd.io + description: "Group is the group of the referent. \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) Support: Custom (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 (or empty string), this refers to the + local namespace of the Route. \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: [] diff --git a/charts/buoyant/linkerd-crds/2024.10.4/templates/policy/meshtls-authentication.yaml b/charts/buoyant/linkerd-crds/2024.10.4/templates/policy/meshtls-authentication.yaml new file mode 100644 index 000000000..58ee815f5 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/templates/policy/meshtls-authentication.yaml @@ -0,0 +1,87 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: meshtlsauthentications.policy.linkerd.io + annotations: + {{ include "partials.annotations.created-by" . }} + labels: + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + linkerd.io/control-plane-ns: {{.Release.Namespace}} +spec: + group: policy.linkerd.io + scope: Namespaced + names: + kind: MeshTLSAuthentication + plural: meshtlsauthentications + singular: meshtlsauthentication + shortNames: [meshtlsauthn] + versions: + - name: v1alpha1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + required: [spec] + properties: + spec: + description: >- + MeshTLSAuthentication defines a list of authenticated client IDs + to be referenced by an `AuthorizationPolicy`. If a client + connection has the mutually-authenticated identity that matches + ANY of the of the provided identities, the connection is + considered authenticated. + type: object + oneOf: + - required: [identities] + - required: [identityRefs] + properties: + identities: + description: >- + Authorizes clients with the provided proxy identity strings + (as provided via MTLS) + + The `*` prefix can be used to match all identities in + a domain. An identity string of `*` indicates that + all authentication clients are authorized. + type: array + minItems: 1 + items: + type: string + identityRefs: + type: array + minItems: 1 + items: + type: object + required: + - kind + properties: + group: + description: >- + Group is the group of the referent. When empty, the + Kubernetes 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 the 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. When unspecified, + this refers to all resources of the specified Group + and Kind in the specified namespace. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: >- + Name is the name of the referent. When unspecified, + this authentication refers to the local namespace. + maxLength: 253 + type: string diff --git a/charts/buoyant/linkerd-crds/2024.10.4/templates/policy/network-authentication.yaml b/charts/buoyant/linkerd-crds/2024.10.4/templates/policy/network-authentication.yaml new file mode 100644 index 000000000..cef15d3c4 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/templates/policy/network-authentication.yaml @@ -0,0 +1,53 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: networkauthentications.policy.linkerd.io + annotations: + {{ include "partials.annotations.created-by" . }} + labels: + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + linkerd.io/control-plane-ns: {{.Release.Namespace}} +spec: + group: policy.linkerd.io + scope: Namespaced + names: + kind: NetworkAuthentication + plural: networkauthentications + singular: networkauthentication + shortNames: [netauthn, networkauthn] + versions: + - name: v1alpha1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + required: [spec] + properties: + spec: + description: >- + NetworkAuthentication defines a list of authenticated client + networks to be referenced by an `AuthorizationPolicy`. If a + client connection originates from ANY of the of the provided + networks, the connection is considered authenticated. + type: object + required: [networks] + properties: + networks: + type: array + items: + type: object + required: [cidr] + properties: + cidr: + description: >- + The CIDR of the network to be authorized. + type: string + except: + description: >- + A list of IP networks/addresses not to be included in + the above `cidr`. + type: array + items: + type: string diff --git a/charts/buoyant/linkerd-crds/2024.10.4/templates/policy/server-authorization.yaml b/charts/buoyant/linkerd-crds/2024.10.4/templates/policy/server-authorization.yaml new file mode 100644 index 000000000..33fb65900 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/templates/policy/server-authorization.yaml @@ -0,0 +1,266 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: serverauthorizations.policy.linkerd.io + annotations: + {{ include "partials.annotations.created-by" . }} + labels: + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + linkerd.io/control-plane-ns: {{.Release.Namespace}} +spec: + group: policy.linkerd.io + scope: Namespaced + names: + kind: ServerAuthorization + plural: serverauthorizations + singular: serverauthorization + shortNames: [saz, serverauthz, srvauthz] + versions: + - name: v1alpha1 + served: true + storage: false + deprecated: true + deprecationWarning: "policy.linkerd.io/v1alpha1 ServerAuthorization is deprecated; use policy.linkerd.io/v1beta1 ServerAuthorization" + schema: + openAPIV3Schema: + type: object + required: [spec] + properties: + spec: + description: >- + Authorizes clients to communicate with Linkerd-proxied servers. + type: object + required: [server, client] + properties: + server: + description: >- + Identifies servers in the same namespace for which this + authorization applies. + + Only one of `name` or `selector` may be specified. + type: object + oneOf: + - required: [name] + - required: [selector] + properties: + name: + description: References a `Server` instance by name + type: string + pattern: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$' + selector: + description: >- + A label query over servers on which this authorization applies. + type: object + properties: + matchLabels: + type: object + x-kubernetes-preserve-unknown-fields: true + matchExpressions: + type: array + items: + type: object + required: [key, operator] + properties: + key: + type: string + operator: + type: string + enum: [In, NotIn, Exists, DoesNotExist] + values: + type: array + items: + type: string + client: + description: Describes clients authorized to access a server. + type: object + properties: + networks: + description: >- + Limits the client IP addresses to which this + authorization applies. If unset, the server chooses a + default (typically, all IPs or the cluster's pod + network). + type: array + items: + type: object + required: [cidr] + properties: + cidr: + type: string + except: + type: array + items: + type: string + unauthenticated: + description: >- + Authorizes unauthenticated clients to access a server. + type: boolean + meshTLS: + type: object + properties: + unauthenticatedTLS: + type: boolean + description: >- + Indicates that no client identity is required for + communication. + + This is mostly important for the identity + controller, which must terminate TLS connections + from clients that do not yet have a certificate. + identities: + description: >- + Authorizes clients with the provided proxy identity + strings (as provided via MTLS) + + The `*` prefix can be used to match all identities in + a domain. An identity string of `*` indicates that + all authentication clients are authorized. + type: array + items: + type: string + pattern: '^(\*|[a-z0-9]([-a-z0-9]*[a-z0-9])?)(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$' + serviceAccounts: + description: >- + Authorizes clients with the provided proxy identity + service accounts (as provided via MTLS) + type: array + items: + type: object + required: [name] + properties: + name: + description: The ServiceAccount's name. + type: string + pattern: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$' + namespace: + description: >- + The ServiceAccount's namespace. If unset, the + authorization's namespace is used. + type: string + pattern: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$' + - name: v1beta1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + required: [spec] + properties: + spec: + description: >- + Authorizes clients to communicate with Linkerd-proxied servers. + type: object + required: [server, client] + properties: + server: + description: >- + Identifies servers in the same namespace for which this + authorization applies. + + Only one of `name` or `selector` may be specified. + type: object + oneOf: + - required: [name] + - required: [selector] + properties: + name: + description: References a `Server` instance by name + type: string + pattern: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$' + selector: + description: >- + A label query over servers on which this authorization applies. + type: object + properties: + matchLabels: + type: object + x-kubernetes-preserve-unknown-fields: true + matchExpressions: + type: array + items: + type: object + required: [key, operator] + properties: + key: + type: string + operator: + type: string + enum: [In, NotIn, Exists, DoesNotExist] + values: + type: array + items: + type: string + client: + description: Describes clients authorized to access a server. + type: object + properties: + networks: + description: >- + Limits the client IP addresses to which this + authorization applies. If unset, the server chooses a + default (typically, all IPs or the cluster's pod + network). + type: array + items: + type: object + required: [cidr] + properties: + cidr: + type: string + except: + type: array + items: + type: string + unauthenticated: + description: >- + Authorizes unauthenticated clients to access a server. + type: boolean + meshTLS: + type: object + properties: + unauthenticatedTLS: + type: boolean + description: >- + Indicates that no client identity is required for + communication. + + This is mostly important for the identity + controller, which must terminate TLS connections + from clients that do not yet have a certificate. + identities: + description: >- + Authorizes clients with the provided proxy identity + strings (as provided via MTLS) + + The `*` prefix can be used to match all identities in + a domain. An identity string of `*` indicates that + all authentication clients are authorized. + type: array + items: + type: string + pattern: '^(\*|[a-z0-9]([-a-z0-9]*[a-z0-9])?)(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$' + serviceAccounts: + description: >- + Authorizes clients with the provided proxy identity + service accounts (as provided via MTLS) + type: array + items: + type: object + required: [name] + properties: + name: + description: The ServiceAccount's name. + type: string + pattern: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$' + namespace: + description: >- + The ServiceAccount's namespace. If unset, the + authorization's namespace is used. + type: string + pattern: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$' + additionalPrinterColumns: + - name: Server + type: string + description: The server that this grants access to + jsonPath: .spec.server.name diff --git a/charts/buoyant/linkerd-crds/2024.10.4/templates/policy/server.yaml b/charts/buoyant/linkerd-crds/2024.10.4/templates/policy/server.yaml new file mode 100644 index 000000000..3de5a34e0 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/templates/policy/server.yaml @@ -0,0 +1,319 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: servers.policy.linkerd.io + annotations: + {{ include "partials.annotations.created-by" . }} + labels: + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + linkerd.io/control-plane-ns: {{.Release.Namespace}} +spec: + group: policy.linkerd.io + names: + kind: Server + plural: servers + singular: server + shortNames: [srv] + scope: Namespaced + versions: + - name: v1alpha1 + served: true + storage: false + deprecated: true + deprecationWarning: "policy.linkerd.io/v1alpha1 Server is deprecated; use policy.linkerd.io/v1beta1 Server" + schema: + openAPIV3Schema: + type: object + required: [spec] + properties: + spec: + type: object + required: + - podSelector + - port + properties: + podSelector: + type: object + description: >- + Selects pods in the same namespace. + oneOf: + - required: [matchExpressions] + - required: [matchLabels] + properties: + matchLabels: + type: object + x-kubernetes-preserve-unknown-fields: true + matchExpressions: + type: array + items: + type: object + required: [key, operator] + properties: + key: + type: string + operator: + type: string + enum: [In, NotIn, Exists, DoesNotExist] + values: + type: array + items: + type: string + port: + description: >- + A port name or number. Must exist in a pod spec. + x-kubernetes-int-or-string: true + proxyProtocol: + description: >- + Configures protocol discovery for inbound connections. + + Supersedes the `config.linkerd.io/opaque-ports` annotation. + type: string + default: unknown + - name: v1beta1 + served: true + storage: false + deprecated: true + deprecationWarning: "policy.linkerd.io/v1beta1 Server is deprecated; use policy.linkerd.io/v1beta3 Server" + schema: + openAPIV3Schema: + type: object + required: [spec] + properties: + spec: + type: object + required: + - podSelector + - port + properties: + podSelector: + type: object + description: >- + Selects pods in the same namespace. + + The result of matchLabels and matchExpressions are ANDed. + Selects all if empty. + properties: + matchLabels: + type: object + x-kubernetes-preserve-unknown-fields: true + matchExpressions: + type: array + items: + type: object + required: [key, operator] + properties: + key: + type: string + operator: + type: string + enum: [In, NotIn, Exists, DoesNotExist] + values: + type: array + items: + type: string + port: + description: >- + A port name or number. Must exist in a pod spec. + x-kubernetes-int-or-string: true + proxyProtocol: + description: >- + Configures protocol discovery for inbound connections. + + Supersedes the `config.linkerd.io/opaque-ports` annotation. + type: string + default: unknown + additionalPrinterColumns: + - name: Port + type: string + description: The port the server is listening on + jsonPath: .spec.port + - name: Protocol + type: string + description: The protocol of the server + jsonPath: .spec.proxyProtocol + - name: v1beta2 + served: true + storage: false + schema: + openAPIV3Schema: + type: object + required: [spec] + properties: + spec: + type: object + required: + - port + oneOf: + - required: [podSelector] + - required: [externalWorkloadSelector] + properties: + podSelector: + type: object + description: >- + Selects pods in the same namespace. + + The result of matchLabels and matchExpressions are ANDed. + Selects all if empty. + properties: + matchLabels: + type: object + x-kubernetes-preserve-unknown-fields: true + matchExpressions: + type: array + items: + type: object + required: [key, operator] + properties: + key: + type: string + operator: + type: string + enum: [In, NotIn, Exists, DoesNotExist] + values: + type: array + items: + type: string + externalWorkloadSelector: + type: object + description: >- + Selects ExternalWorkloads in the same namespace. + + The result of matchLabels and matchExpressions are ANDed. + Selects all if empty. + properties: + matchLabels: + type: object + x-kubernetes-preserve-unknown-fields: true + matchExpressions: + type: array + items: + type: object + required: [key, operator] + properties: + key: + type: string + operator: + type: string + enum: [In, NotIn, Exists, DoesNotExist] + values: + type: array + items: + type: string + port: + description: >- + A port name or number. Must exist in a pod spec. + x-kubernetes-int-or-string: true + proxyProtocol: + description: >- + Configures protocol discovery for inbound connections. + + Supersedes the `config.linkerd.io/opaque-ports` annotation. + type: string + default: unknown + additionalPrinterColumns: + - name: Port + type: string + description: The port the server is listening on + jsonPath: .spec.port + - name: Protocol + type: string + description: The protocol of the server + jsonPath: .spec.proxyProtocol + - name: v1beta3 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + required: [spec] + properties: + spec: + type: object + required: + - port + oneOf: + - required: [podSelector] + - required: [externalWorkloadSelector] + properties: + accessPolicy: + type: string + default: deny + description: >- + Default access policy to apply when the traffic doesn't match any of the policy rules. + podSelector: + type: object + description: >- + Selects pods in the same namespace. + + The result of matchLabels and matchExpressions are ANDed. + Selects all if empty. + properties: + matchLabels: + type: object + x-kubernetes-preserve-unknown-fields: true + matchExpressions: + type: array + items: + type: object + required: [key, operator] + properties: + key: + type: string + operator: + type: string + enum: [In, NotIn, Exists, DoesNotExist] + values: + type: array + items: + type: string + externalWorkloadSelector: + type: object + description: >- + Selects ExternalWorkloads in the same namespace. + + The result of matchLabels and matchExpressions are ANDed. + Selects all if empty. + properties: + matchLabels: + type: object + x-kubernetes-preserve-unknown-fields: true + matchExpressions: + type: array + items: + type: object + required: [key, operator] + properties: + key: + type: string + operator: + type: string + enum: [In, NotIn, Exists, DoesNotExist] + values: + type: array + items: + type: string + port: + description: >- + A port name or number. Must exist in a pod spec. + x-kubernetes-int-or-string: true + proxyProtocol: + description: >- + Configures protocol discovery for inbound connections. + + Supersedes the `config.linkerd.io/opaque-ports` annotation. + type: string + default: unknown + additionalPrinterColumns: + - name: Port + type: string + description: The port the server is listening on + jsonPath: .spec.port + - name: Protocol + type: string + description: The protocol of the server + jsonPath: .spec.proxyProtocol + - name: Access Policy + type: string + description: The default access policy applied when the traffic doesn't match any of the policy rules + jsonPath: .spec.accessPolicy diff --git a/charts/buoyant/linkerd-crds/2024.10.4/templates/serviceprofile.yaml b/charts/buoyant/linkerd-crds/2024.10.4/templates/serviceprofile.yaml new file mode 100644 index 000000000..ad12c96a3 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/templates/serviceprofile.yaml @@ -0,0 +1,274 @@ +--- +### +### Service Profile CRD +### +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: serviceprofiles.linkerd.io + annotations: + {{ include "partials.annotations.created-by" . }} + labels: + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + linkerd.io/control-plane-ns: {{.Release.Namespace}} +spec: + group: linkerd.io + versions: + - name: v1alpha1 + served: true + storage: false + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + description: Spec is the custom resource spec + required: + - routes + properties: + dstOverrides: + type: array + required: + - authority + - weight + items: + type: object + description: WeightedDst is a weighted alternate destination. + properties: + authority: + type: string + weight: + x-kubernetes-int-or-string: true + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + opaquePorts: + type: array + items: + type: string + retryBudget: + type: object + required: + - minRetriesPerSecond + - retryRatio + - ttl + description: RetryBudget describes the maximum number of retries that should be issued to this service. + properties: + minRetriesPerSecond: + format: int32 + type: integer + retryRatio: + type: number + format: float + ttl: + type: string + routes: + type: array + items: + type: object + description: RouteSpec specifies a Route resource. + required: + - condition + - name + properties: + condition: + type: object + description: RequestMatch describes the conditions under which to match a Route. + properties: + pathRegex: + type: string + method: + type: string + all: + type: array + items: + type: object + x-kubernetes-preserve-unknown-fields: true + any: + type: array + items: + type: object + x-kubernetes-preserve-unknown-fields: true + not: + type: array + items: + type: object + x-kubernetes-preserve-unknown-fields: true + isRetryable: + type: boolean + name: + type: string + timeout: + type: string + responseClasses: + type: array + items: + type: object + required: + - condition + description: ResponseClass describes how to classify a response (e.g. success or failures). + properties: + condition: + type: object + description: ResponseMatch describes the conditions under + which to classify a response. + properties: + all: + type: array + items: + type: object + x-kubernetes-preserve-unknown-fields: true + any: + type: array + items: + type: object + x-kubernetes-preserve-unknown-fields: true + not: + type: object + x-kubernetes-preserve-unknown-fields: true + status: + type: object + description: Range describes a range of integers (e.g. status codes). + properties: + max: + format: int32 + type: integer + min: + format: int32 + type: integer + isFailure: + type: boolean + - name: v1alpha2 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + description: Spec is the custom resource spec + properties: + dstOverrides: + type: array + required: + - authority + - weight + items: + type: object + description: WeightedDst is a weighted alternate destination. + properties: + authority: + type: string + weight: + x-kubernetes-int-or-string: true + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + opaquePorts: + type: array + items: + type: string + retryBudget: + type: object + required: + - minRetriesPerSecond + - retryRatio + - ttl + description: RetryBudget describes the maximum number of retries that should be issued to this service. + properties: + minRetriesPerSecond: + format: int32 + type: integer + retryRatio: + type: number + format: float + ttl: + type: string + routes: + type: array + items: + type: object + description: RouteSpec specifies a Route resource. + required: + - condition + - name + properties: + condition: + type: object + description: RequestMatch describes the conditions under which to match a Route. + properties: + pathRegex: + type: string + method: + type: string + all: + type: array + items: + type: object + x-kubernetes-preserve-unknown-fields: true + any: + type: array + items: + type: object + x-kubernetes-preserve-unknown-fields: true + not: + type: array + items: + type: object + x-kubernetes-preserve-unknown-fields: true + isRetryable: + type: boolean + name: + type: string + timeout: + type: string + responseClasses: + type: array + items: + type: object + required: + - condition + description: ResponseClass describes how to classify a response (e.g. success or failures). + properties: + condition: + type: object + description: ResponseMatch describes the conditions under + which to classify a response. + properties: + all: + type: array + items: + type: object + x-kubernetes-preserve-unknown-fields: true + any: + type: array + items: + type: object + x-kubernetes-preserve-unknown-fields: true + not: + type: object + x-kubernetes-preserve-unknown-fields: true + status: + type: object + description: Range describes a range of integers (e.g. status codes). + properties: + max: + format: int32 + type: integer + min: + format: int32 + type: integer + isFailure: + type: boolean + scope: Namespaced + preserveUnknownFields: false + names: + plural: serviceprofiles + singular: serviceprofile + kind: ServiceProfile + shortNames: + - sp diff --git a/charts/buoyant/linkerd-crds/2024.10.4/templates/workload/external-workload.yaml b/charts/buoyant/linkerd-crds/2024.10.4/templates/workload/external-workload.yaml new file mode 100644 index 000000000..2e6e43ae6 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/templates/workload/external-workload.yaml @@ -0,0 +1,303 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: externalworkloads.workload.linkerd.io + annotations: + {{ include "partials.annotations.created-by" . }} + labels: + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + linkerd.io/control-plane-ns: {{.Release.Namespace}} +spec: + group: workload.linkerd.io + names: + categories: + - external + kind: ExternalWorkload + listKind: ExternalWorkloadList + plural: externalworkloads + singular: externalworkload + scope: Namespaced + versions: + - name: v1alpha1 + served: true + storage: false + schema: + openAPIV3Schema: + description: >- + An ExternalWorkload describes a single workload (i.e. a deployable unit) external + to the cluster that should be enrolled in the mesh. + type: object + required: [spec] + properties: + apiVerson: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + meshTls: + description: meshTls describes TLS settings associated with an + external workload. + properties: + identity: + type: string + description: identity of the workload. Corresponds to the + identity used in the workload's certificate. It is used + by peers to perform verification in the mTLS handshake. + minLength: 1 + maxLength: 253 + serverName: + type: string + description: serverName is the name of the workload in DNS + format. It is used by the workload to terminate TLS using + SNI. + minLength: 1 + maxLength: 253 + type: object + required: + - identity + - serverName + ports: + type: array + description: ports describes a list of ports exposed by the + workload + items: + properties: + name: + type: string + description: name must be an IANA_SVC_NAME and unique + within the ports set. Each named port can be referred + to by services. + port: + format: int32 + maximum: 65535 + minimum: 1 + type: integer + protocol: + description: protocol exposed by the port. Must be UDP or + TCP. Defaults to TCP. + type: string + default: "TCP" + type: object + required: + - port + workloadIPs: + type: array + description: workloadIPs contains a list of IP addresses that + can be used to send traffic to the workload. + items: + type: object + properties: + ip: + type: string + # TODO: relax this in the future when ipv6 is supported + # an external workload (like a pod) should only + # support 2 interfaces + maxItems: 1 + type: object + required: + - meshTls + status: + type: object + properties: + conditions: + type: array + items: + type: object + properties: + lastProbeTime: + description: lastProbeTime is the last time the + healthcheck endpoint was probed. + format: date-time + type: string + lastTransitionTime: + description: lastTransitionTime is the last time the + condition transitioned from one status to another. + format: date-time + type: string + status: + description: status of the condition (one of True, False, Unknown) + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of the condition in CamelCase or in + foo.example.com/CamelCase. + 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 + 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 + message: + description: message is a human readable message + indicating details about the transition. This may be an + empty string. + maxLength: 32768 + type: string + required: + - status + - type + additionalPrinterColumns: + - jsonPath: .spec.meshTls.identity + name: Identity + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - name: v1beta1 + served: true + storage: true + subresources: + status: {} + schema: + openAPIV3Schema: + description: >- + An ExternalWorkload describes a single workload (i.e. a deployable unit) external + to the cluster that should be enrolled in the mesh. + type: object + required: [spec] + properties: + apiVerson: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + meshTLS: + description: meshTLS describes TLS settings associated with an + external workload. + properties: + identity: + type: string + description: identity of the workload. Corresponds to the + identity used in the workload's certificate. It is used + by peers to perform verification in the mTLS handshake. + minLength: 1 + maxLength: 253 + serverName: + type: string + description: serverName is the name of the workload in DNS + format. It is used by the workload to terminate TLS using + SNI. + minLength: 1 + maxLength: 253 + type: object + required: + - identity + - serverName + ports: + type: array + description: ports describes a list of ports exposed by the + workload + items: + properties: + name: + type: string + description: name must be an IANA_SVC_NAME and unique + within the ports set. Each named port can be referred + to by services. + port: + format: int32 + maximum: 65535 + minimum: 1 + type: integer + protocol: + description: protocol exposed by the port. Must be UDP or + TCP. Defaults to TCP. + type: string + default: "TCP" + type: object + required: + - port + workloadIPs: + type: array + description: workloadIPs contains a list of IP addresses that + can be used to send traffic to the workload. This field may + hold a maximum of two entries. If one entry, it can be an + IPv4 or IPv6 address; if two entries it should contain one + IPv4 address and one IPv6 address. + items: + type: object + properties: + ip: + type: string + maxItems: 2 + type: object + required: + - meshTLS + status: + type: object + properties: + conditions: + type: array + items: + type: object + properties: + lastProbeTime: + description: lastProbeTime is the last time the + healthcheck endpoint was probed. + format: date-time + type: string + lastTransitionTime: + description: lastTransitionTime is the last time the + condition transitioned from one status to another. + format: date-time + type: string + status: + description: status of the condition (one of True, False, Unknown) + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of the condition in CamelCase or in + foo.example.com/CamelCase. + 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 + 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 + message: + description: message is a human readable message + indicating details about the transition. This may be an + empty string. + maxLength: 32768 + type: string + required: + - status + - type + additionalPrinterColumns: + - jsonPath: .spec.meshTLS.identity + name: Identity + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date diff --git a/charts/buoyant/linkerd-crds/2024.10.4/values.yaml b/charts/buoyant/linkerd-crds/2024.10.4/values.yaml new file mode 100644 index 000000000..2cc17719e --- /dev/null +++ b/charts/buoyant/linkerd-crds/2024.10.4/values.yaml @@ -0,0 +1,3 @@ +enableHttpRoutes: true +enableTlsRoutes: true +enableTcpRoutes: true diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/.helmignore b/charts/dynatrace/dynatrace-operator/1.3.2/.helmignore new file mode 100644 index 000000000..98229532e --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/.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/ + +tests/ diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/Chart.yaml b/charts/dynatrace/dynatrace-operator/1.3.2/Chart.yaml new file mode 100644 index 000000000..7c8b93ddd --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/Chart.yaml @@ -0,0 +1,23 @@ +annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Dynatrace Operator + catalog.cattle.io/kube-version: '>=1.19.0-0' + catalog.cattle.io/release-name: dynatrace-operator +apiVersion: v2 +appVersion: 1.3.2 +description: The Dynatrace Operator Helm chart for Kubernetes and OpenShift +home: https://www.dynatrace.com/ +icon: file://assets/icons/dynatrace-operator.png +kubeVersion: '>=1.19.0-0' +maintainers: +- email: marcell.sevcsik@dynatrace.com + name: 0sewa0 +- email: christoph.muellner@dynatrace.com + name: chrismuellner +- email: lukas.hinterreiter@dynatrace.com + name: luhi-DT +name: dynatrace-operator +sources: +- https://github.com/Dynatrace/dynatrace-operator +type: application +version: 1.3.2 diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/README.md b/charts/dynatrace/dynatrace-operator/1.3.2/README.md new file mode 100644 index 000000000..97a98a018 --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/README.md @@ -0,0 +1,48 @@ +# Dynatrace Operator Helm Chart + +The Dynatrace Operator supports rollout and lifecycle of various Dynatrace components in Kubernetes and OpenShift. + +This Helm Chart requires Helm 3. + +## Quick Start + +Migration instructions can be found in the [official help page](https://www.dynatrace.com/support/help/shortlink/k8s-dto-helm#migrate). + +Install the Dynatrace Operator via Helm by running the following commands. + +### Installation + +> For instructions on how to install the dynatrace-operator on Openshift, head to the +> [official help page](https://www.dynatrace.com/support/help/shortlink/k8s-helm) + +#### For versions older than 0.15.0 + +Add `dynatrace` helm repository: + +```console +helm repo add dynatrace https://raw.githubusercontent.com/Dynatrace/dynatrace-operator/main/config/helm/repos/stable +``` + +Install `dynatrace-operator` helm chart and create the corresponding `dynatrace` namespace: + +```console +helm install dynatrace-operator dynatrace/dynatrace-operator -n dynatrace --create-namespace --atomic +``` + +#### For versions 0.15.0 and after + +Install `dynatrace-operator` helm chart using the OCI repository and create the corresponding `dynatrace` namespace: + +```console +helm install dynatrace-operator oci://public.ecr.aws/dynatrace/dynatrace-operator -n dynatrace --create-namespace --atomic +``` + +## Uninstall chart + +> Full instructions can be found in the [official help page](https://www.dynatrace.com/support/help/shortlink/k8s-helm#uninstall-dynatrace-operator) + +Uninstall the Dynatrace Operator by running the following command: + +```console +helm uninstall dynatrace-operator -n dynatrace +``` diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/app-readme.md b/charts/dynatrace/dynatrace-operator/1.3.2/app-readme.md new file mode 100644 index 000000000..844c96dd7 --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/app-readme.md @@ -0,0 +1,5 @@ +# Dynatrace Operator + +The Dynatrace Operator supports rollout and lifecycle of various Dynatrace components in Kubernetes and OpenShift. + +As of launch, the Dynatrace Operator can be used to deploy a containerized ActiveGate for Kubernetes API monitoring. New capabilities will be added to the Dynatrace Operator over time including metric routing, and API monitoring for AWS, Azure, GCP, and vSphere. diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/logo.png b/charts/dynatrace/dynatrace-operator/1.3.2/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..6714eb8a59509d9513133b43b2de290f73be9c18 GIT binary patch literal 9908 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 R5fVsXi>>{}+<&~z{~v%ki3tDz literal 0 HcmV?d00001 diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/questions.yml b/charts/dynatrace/dynatrace-operator/1.3.2/questions.yml new file mode 100644 index 000000000..70c94f9da --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/questions.yml @@ -0,0 +1,236 @@ +categories: + - APM + - Monitoring +questions: + + #################### Global Configuration #################### + - variable: installCRD + label: "Install Custom Resource Definitions" + description: "Installs the Custom Resource Definitions for the Dynakube. This is recommended if you haven't installed it manually yet. Default: true" + default: true + type: boolean + group: "Global Configuration" + + - variable: image + label: "Set a custom image for operator components" + description: "Set a custom image for operator. Defaults to public.ecr.aws/dynatrace/dynatrace-operator" + default: "" + type: string + group: "Global Configuration" + + - variable: customPullSecret + label: "Set a custom pull secret for operator image" + description: "Set a custom pull secret for the operator image" + default: "" + type: string + group: "Global Configuration" + + #################### Operator Deployment Configuration #################### + - variable: operator.nodeSelector + label: "Assign the Dynatrace Operator's pod to certain nodes" + description: "Defines a NodeSelector to customize to which nodes the Dynatrace Operator can be deployed on - Please edit as Yaml for the best experience - see https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector" + default: "" + type: string + group: "Operator Deployment Configuration" + + - variable: operator.tolerations + label: "Custom tolerations for the Dynatrace Operator's pod" + description: "Defines custom tolerations to the Dynatrace Operator - Please edit as Yaml for the best experience - see https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/" + default: "" + type: string + group: "Operator Deployment Configuration" + + - variable: operator.apparmor + label: "Enable AppArmor for the Dynatrace Operator's pod" + description: "Adds AppArmor security annotations to the Dynatrace Operator's pod. Default: false" + default: false + type: boolean + group: "Operator Deployment Configuration" + + - variable: operator.requests.cpu + label: "CPU resource requests settings for Dynatrace Operator's pods" + description: "The minimum amount of CPU resources that the Dynatrace Operator's pods should request. Affects scheduling. Default: 50m" + default: "50m" + type: string + group: "Operator Deployment Configuration" + + - variable: operator.requests.memory + label: "Memory resource requests settings for Dynatrace Operator's pods" + description: "The minimum amount of memory that the Dynatrace Operator's pods should request. Affects scheduling. Default: 64Mi" + default: "64Mi" + type: string + group: "Operator Deployment Configuration" + + - variable: operator.limits.cpu + label: "CPU resource limits settings for Dynatrace Operator's pods" + description: "The maximum amount of CPU resources that the Dynatrace Operator's pods can use. Default: 100m" + default: "100m" + type: string + group: "Operator Deployment Configuration" + + - variable: operator.limits.memory + label: "Memory resource limits settings for Dynatrace Operator's pods" + description: "The maximum amount of memory that the Dynatrace Operator's pods can use. Pod restarted if exceeded. Default: 128Mi" + default: "128Mi" + type: string + group: "Operator Deployment Configuration" + + + #################### Webhook Deployment Configuration #################### + + - variable: webhook.apparmor + label: "Enable AppArmor for the Dynatrace Webhook's pod" + description: "Adds AppArmor security annotations to the Dynatrace Webhook's pod. Default: false" + default: false + type: boolean + group: "Webhook Deployment Configuration" + + - variable: webhook.highAvailability + label: "Enable high availability for the Dynatrace Webhook's pod" + description: "Adds topologySpreadConstraints and increases the replicas to 2 for the Dynatrace Webhook's pod. Default: false" + default: false + type: boolean + group: "Webhook Deployment Configuration" + + - variable: webhook.hostNetwork + label: "Enable hostNetwork for the Dynatrace Webhook's pod" + description: "Enables hostNetwork for the Dynatrace Webhook's pod. Default: false" + default: false + type: boolean + group: "Webhook Deployment Configuration" + + - variable: webhook.requests.cpu + label: "CPU resource requests settings for Dynatrace Webhook's pods" + description: "The minimum amount of CPU resources that the Dynatrace Webhook's pods should request. Affects scheduling. Default: 300m" + default: "300m" + type: string + group: "Webhook Deployment Configuration" + + - variable: webhook.requests.memory + label: "Memory resource requests settings for Dynatrace Webhook's pods" + description: "The minimum amount of memory that the Dynatrace Webhook's pods should request. Affects scheduling. Default: 128Mi" + default: "128Mi" + type: string + group: "Webhook Deployment Configuration" + + - variable: webhook.limits.cpu + label: "CPU resource limits settings for Dynatrace Webhook's pods" + description: "The maximum amount of CPU resources that the Dynatrace Webhook's pods can use. Default: 300m" + default: "300m" + type: string + group: "Webhook Deployment Configuration" + + - variable: webhook.limits.memory + label: "Memory resource limits settings for Dynatrace Webhook's pods" + description: "The maximum amount of memory that the Dynatrace Webhook's pods can use. Pod restarted if exceeded. Default: 128Mi" + default: "128Mi" + type: string + group: "Webhook Deployment Configuration" + + + #################### CSI Driver Deployment Configuration #################### + + - variable: csidriver.enabled + label: "Deploy the Dynatrace CSI Driver" + description: "Deploys the Dynatrace CSI Driver via a DaemonSet to enable Cloud Native FullStack. Default: false" + default: false + type: boolean + group: "CSI Driver Deployment Configuration" + + - variable: csidriver.server.requests.cpu + label: "CPU resource requests settings for Dynatrace CSI Driver's server container" + description: "The minimum amount of CPU resources that the Dynatrace CSI Driver's server container should request. Affects scheduling. Default: 50m" + default: "50m" + type: string + group: "CSI Driver Deployment Configuration" + + - variable: csidriver.server.requests.memory + label: "Memory resource requests settings for Dynatrace CSI Driver's server container" + description: "The minimum amount of memory that the Dynatrace CSI Driver's server container should request. Affects scheduling. Default: 100Mi" + default: "100Mi" + type: string + group: "CSI Driver Deployment Configuration" + + - variable: csidriver.server.limits.cpu + label: "CPU resource limits settings for Dynatrace CSI Driver's server container" + description: "The maximum amount of CPU resources that the Dynatrace CSI Driver's server container can use. Default: 50m" + default: "50m" + type: string + group: "CSI Driver Deployment Configuration" + + - variable: csidriver.server.limits.memory + label: "Memory resource limits settings for Dynatrace CSI Driver's server container" + description: "The maximum amount of memory that the Dynatrace CSI Driver's server container can use. Pod restarted if exceeded. Default: 100Mi" + default: "100Mi" + type: string + group: "CSI Driver Deployment Configuration" + + - variable: csidriver.provisioner.requests.cpu + label: "CPU resource requests settings for Dynatrace CSI Driver's provisioner container" + description: "The minimum amount of CPU resources that the Dynatrace CSI Driver's provisioner container should request. Affects scheduling. Default: 300m" + default: "300m" + type: string + group: "CSI Driver Deployment Configuration" + + - variable: csidriver.provisioner.requests.memory + label: "Memory resource requests settings for Dynatrace CSI Driver's provisioner container" + description: "The minimum amount of memory that the Dynatrace CSI Driver's provisioner container should request. Affects scheduling. Default: 100Mi" + default: "100Mi" + type: string + group: "CSI Driver Deployment Configuration" + + - variable: csidriver.registrar.requests.cpu + label: "CPU resource requests settings for Dynatrace CSI Driver's registrar container" + description: "The minimum amount of CPU resources that the Dynatrace CSI Driver's registrar container should request. Affects scheduling. Default: 20m" + default: "20m" + type: string + group: "CSI Driver Deployment Configuration" + + - variable: csidriver.registrar.requests.memory + label: "Memory resource requests settings for Dynatrace CSI Driver's registrar container" + description: "The minimum amount of memory that the Dynatrace CSI Driver's registrar container should request. Affects scheduling. Default: 30Mi" + default: "30Mi" + type: string + group: "CSI Driver Deployment Configuration" + + - variable: csidriver.registrar.limits.cpu + label: "CPU resource limits settings for Dynatrace CSI Driver's registrar container" + description: "The maximum amount of CPU resources that the Dynatrace CSI Driver's registrar container can use. Default: 20m" + default: "20m" + type: string + group: "CSI Driver Deployment Configuration" + + - variable: csidriver.registrar.limits.memory + label: "Memory resource limits settings for Dynatrace CSI Driver's registrar container" + description: "The maximum amount of memory that the Dynatrace CSI Driver's registrar container can use. Pod restarted if exceeded. Default: 30Mi" + default: "30Mi" + type: string + group: "CSI Driver Deployment Configuration" + + - variable: csidriver.livenessprobe.requests.cpu + label: "CPU resource requests settings for Dynatrace CSI Driver's livenessprobe container" + description: "The minimum amount of CPU resources that the Dynatrace CSI Driver's livenessprobe container should request. Affects scheduling. Default: 20m" + default: "20m" + type: string + group: "CSI Driver Deployment Configuration" + + - variable: csidriver.livenessprobe.requests.memory + label: "Memory resource requests settings for Dynatrace CSI Driver's livenessprobe container" + description: "The minimum amount of memory that the Dynatrace CSI Driver's livenessprobe container should request. Affects scheduling. Default: 30Mi" + default: "30Mi" + type: string + group: "CSI Driver Deployment Configuration" + + - variable: csidriver.livenessprobe.limits.cpu + label: "CPU resource limits settings for Dynatrace CSI Driver's livenessprobe container" + description: "The maximum amount of CPU resources that the Dynatrace CSI Driver's livenessprobe container can use. Default: 20m" + default: "20m" + type: string + group: "CSI Driver Deployment Configuration" + + - variable: csidriver.livenessprobe.limits.memory + label: "Memory resource limits settings for Dynatrace CSI Driver's livenessprobe container" + description: "The maximum amount of memory that the Dynatrace CSI Driver's livenessprobe container can use. Pod restarted if exceeded. Default: 30Mi" + default: "30Mi" + type: string + group: "CSI Driver Deployment Configuration" diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/activegate/clusterrole-activegate.yaml b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/activegate/clusterrole-activegate.yaml new file mode 100644 index 000000000..9d41f7409 --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/activegate/clusterrole-activegate.yaml @@ -0,0 +1,47 @@ +{{- if (eq (include "dynatrace-operator.openshiftOrOlm" .) "true") }} + +# Copyright 2021 Dynatrace LLC + +# 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. +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: dynatrace-activegate + labels: + {{- include "dynatrace-operator.activegateLabels" . | nindent 4 }} +rules: + - apiGroups: + - security.openshift.io + resourceNames: + - privileged + - nonroot-v2 + resources: + - securitycontextconstraints + verbs: + - use +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: dynatrace-activegate + labels: + {{- include "dynatrace-operator.activegateLabels" . | nindent 4 }} +subjects: + - kind: ServiceAccount + name: dynatrace-activegate + namespace: {{ .Release.Namespace }} +roleRef: + kind: ClusterRole + name: dynatrace-activegate + apiGroup: rbac.authorization.k8s.io +{{- end -}} diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/activegate/serviceaccount-activegate.yaml b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/activegate/serviceaccount-activegate.yaml new file mode 100644 index 000000000..93fe71a3f --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/activegate/serviceaccount-activegate.yaml @@ -0,0 +1,20 @@ +# Copyright 2021 Dynatrace LLC + +# 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. +apiVersion: v1 +kind: ServiceAccount +metadata: + name: dynatrace-activegate + namespace: {{ .Release.Namespace }} + labels: + {{- include "dynatrace-operator.activegateLabels" . | nindent 4 }} diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/crd/dynatrace-operator-crd.yaml b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/crd/dynatrace-operator-crd.yaml new file mode 100644 index 000000000..01dc43bf7 --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/crd/dynatrace-operator-crd.yaml @@ -0,0 +1,5783 @@ +{{ if .Values.installCRD }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + name: dynakubes.dynatrace.com +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + name: dynatrace-webhook + namespace: {{.Release.Namespace}} + path: /convert + conversionReviewVersions: + - v1 + - v1beta1 + group: dynatrace.com + names: + categories: + - dynatrace + kind: DynaKube + listKind: DynaKubeList + plural: dynakubes + shortNames: + - dk + - dks + singular: dynakube + preserveUnknownFields: false + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.apiUrl + name: ApiUrl + type: string + - jsonPath: .status.phase + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: DynaKube is the Schema for the DynaKube 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: DynaKubeSpec defines the desired state of DynaKube + properties: + activeGate: + description: General configuration about ActiveGate instances. + properties: + annotations: + additionalProperties: + type: string + description: Adds additional annotations to the ActiveGate pods + type: object + capabilities: + description: Activegate capabilities enabled (routing, kubernetes-monitoring, + metrics-ingest, dynatrace-api) + items: + type: string + type: array + customProperties: + description: |- + Add a custom properties file by providing it as a value or reference it from a secret + If referenced from a secret, make sure the key is called 'customProperties' + properties: + value: + description: Custom properties value. + nullable: true + type: string + valueFrom: + description: Custom properties secret. + nullable: true + type: string + type: object + dnsPolicy: + description: Sets DNS Policy for the ActiveGate pods + type: string + env: + description: List of environment variables to set for the ActiveGate + 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: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + 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: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + 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 + group: + description: Set activation group for ActiveGate + type: string + image: + description: The ActiveGate container image. Defaults to the latest + ActiveGate image provided by the registry on the tenant + type: string + labels: + additionalProperties: + type: string + description: Adds additional labels for the ActiveGate pods + type: object + nodeSelector: + additionalProperties: + type: string + description: Node selector to control the selection of nodes + type: object + priorityClassName: + description: |- + If specified, indicates the pod's priority. Name must be defined by creating a PriorityClass object with that + name. If not specified the setting will be removed from the StatefulSet. + type: string + replicas: + description: Amount of replicas for your ActiveGates + format: int32 + type: integer + resources: + description: Define resources requests and limits for single ActiveGate + pods + 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 + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + 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 + tlsSecretName: + description: |- + The name of a secret containing ActiveGate TLS cert+key and password. If not set, self-signed certificate is used. + server.p12: certificate+key pair in pkcs12 format + password: passphrase to read server.p12 + type: string + tolerations: + description: Set tolerations for the ActiveGate pods + 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: Adds TopologySpreadConstraints for the ActiveGate + pods + 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 + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select 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. + 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. + 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. + 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. + 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. + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + type: object + apiUrl: + description: |- + Dynatrace apiUrl, including the /api path at the end. For SaaS, set YOUR_ENVIRONMENT_ID to your environment ID. For Managed, change the apiUrl address. + For instructions on how to determine the environment ID and how to configure the apiUrl address, see Environment ID (https://www.dynatrace.com/support/help/get-started/monitoring-environment/environment-id). + type: string + customPullSecret: + description: |- + Defines a custom pull secret in case you use a private registry when pulling images from the Dynatrace environment. + To define a custom pull secret and learn about the expected behavior, see Configure customPullSecret + (https://www.dynatrace.com/support/help/setup-and-configuration/setup-on-container-platforms/kubernetes/get-started-with-kubernetes-monitoring/dto-config-options-k8s#custompullsecret). + type: string + enableIstio: + description: |- + When enabled, and if Istio is installed on the Kubernetes environment, Dynatrace Operator will create the corresponding + VirtualService and ServiceEntry objects to allow access to the Dynatrace Cluster from the OneAgent or ActiveGate. + Disabled by default. + type: boolean + kubernetesMonitoring: + description: Configuration for Kubernetes Monitoring + properties: + customProperties: + description: |- + Add a custom properties file by providing it as a value or reference it from a secret + If referenced from a secret, make sure the key is called 'customProperties' + properties: + value: + description: Custom properties value. + nullable: true + type: string + valueFrom: + description: Custom properties secret. + nullable: true + type: string + type: object + enabled: + description: Enables Capability + type: boolean + env: + description: List of environment variables to set for the ActiveGate + 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: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + 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: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + 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 + group: + description: Set activation group for ActiveGate + type: string + image: + description: The ActiveGate container image. Defaults to the latest + ActiveGate image provided by the registry on the tenant + type: string + labels: + additionalProperties: + type: string + description: Adds additional labels for the ActiveGate pods + type: object + nodeSelector: + additionalProperties: + type: string + description: Node selector to control the selection of nodes + type: object + replicas: + description: Amount of replicas for your ActiveGates + format: int32 + type: integer + resources: + description: Define resources requests and limits for single ActiveGate + pods + 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 + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + 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 + tolerations: + description: Set tolerations for the ActiveGate pods + 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: Adds TopologySpreadConstraints for the ActiveGate + pods + 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 + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select 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. + 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. + 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. + 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. + 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. + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + type: object + namespaceSelector: + description: |- + Applicable only for applicationMonitoring or cloudNativeFullStack configuration types. The namespaces where you want Dynatrace Operator to inject. + For more information, see Configure monitoring for namespaces and pods (https://www.dynatrace.com/support/help/setup-and-configuration/setup-on-container-platforms/kubernetes/get-started-with-kubernetes-monitoring/dto-config-options-k8s#annotate). + 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 + networkZone: + description: Sets a network zone for the OneAgent and ActiveGate pods. + type: string + oneAgent: + description: |- + General configuration about OneAgent instances. + You can't enable more than one module (classicFullStack, cloudNativeFullStack, hostMonitoring, or applicationMonitoring). + properties: + applicationMonitoring: + description: |- + dynatrace-webhook injects into application pods based on labeled namespaces. + Has an optional CSI driver per node via DaemonSet to provide binaries to pods. + nullable: true + properties: + codeModulesImage: + description: The OneAgent image that is used to inject into + Pods. + type: string + initResources: + description: |- + Define resources requests and limits for the initContainer. For details, see Managing resources for containers + (https://kubernetes.io/docs/concepts/configuration/manage-resources-containers). + 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 + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + 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 + useCSIDriver: + description: Set if you want to use the CSIDriver. Don't enable + it if you do not have access to Kubernetes nodes or if you + lack privileges. + type: boolean + version: + description: The OneAgent version to be used. + type: string + type: object + classicFullStack: + description: |- + Has a single OneAgent per node via DaemonSet. + Injection is performed via the same OneAgent DaemonSet. + nullable: true + properties: + annotations: + additionalProperties: + type: string + description: Add custom OneAgent annotations. + type: object + args: + description: |- + Set additional arguments to the OneAgent installer. + For available options, see Linux custom installation (https://www.dynatrace.com/support/help/setup-and-configuration/dynatrace-oneagent/installation-and-operation/linux/installation/customize-oneagent-installation-on-linux). + For the list of limitations, see Limitations (https://www.dynatrace.com/support/help/setup-and-configuration/setup-on-container-platforms/docker/set-up-dynatrace-oneagent-as-docker-container#limitations). + items: + type: string + type: array + x-kubernetes-list-type: set + autoUpdate: + description: |- + Disables automatic restarts of OneAgent pods in case a new version is available (https://www.dynatrace.com/support/help/setup-and-configuration/setup-on-container-platforms/kubernetes/get-started-with-kubernetes-monitoring#disable-auto). + Enabled by default. + type: boolean + dnsPolicy: + description: Set the DNS Policy for OneAgent pods. For details, + see Pods DNS Policy (https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy). + type: string + env: + description: Set additional environment variables for the + OneAgent pods. + 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: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + 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: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + 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: Use a custom OneAgent Docker image. Defaults + to the image from the Dynatrace cluster. + type: string + labels: + additionalProperties: + type: string + description: Your defined labels for OneAgent pods in order + to structure workloads as desired. + type: object + nodeSelector: + additionalProperties: + type: string + description: Specify the node selector that controls on which + nodes OneAgent will be deployed. + type: object + oneAgentResources: + description: |- + Resource settings for OneAgent container. Consumption of the OneAgent heavily depends on the workload to monitor. You can use the default settings in the CR. + Note: resource.requests shows the values needed to run; resource.limits shows the maximum limits for the pod. + 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 + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + 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 + priorityClassName: + description: |- + Assign a priority class to the OneAgent pods. By default, no class is set. + For details, see Pod Priority and Preemption (https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/). + type: string + tolerations: + description: Tolerations to include with the OneAgent DaemonSet. + For details, see Taints and Tolerations (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 + version: + description: The OneAgent version to be used. + type: string + type: object + cloudNativeFullStack: + description: |- + Has a single OneAgent per node via DaemonSet. + dynatrace-webhook injects into application pods based on labeled namespaces. + Has a CSI driver per node via DaemonSet to provide binaries to pods. + nullable: true + properties: + annotations: + additionalProperties: + type: string + description: Add custom OneAgent annotations. + type: object + args: + description: |- + Set additional arguments to the OneAgent installer. + For available options, see Linux custom installation (https://www.dynatrace.com/support/help/setup-and-configuration/dynatrace-oneagent/installation-and-operation/linux/installation/customize-oneagent-installation-on-linux). + For the list of limitations, see Limitations (https://www.dynatrace.com/support/help/setup-and-configuration/setup-on-container-platforms/docker/set-up-dynatrace-oneagent-as-docker-container#limitations). + items: + type: string + type: array + x-kubernetes-list-type: set + autoUpdate: + description: |- + Disables automatic restarts of OneAgent pods in case a new version is available (https://www.dynatrace.com/support/help/setup-and-configuration/setup-on-container-platforms/kubernetes/get-started-with-kubernetes-monitoring#disable-auto). + Enabled by default. + type: boolean + codeModulesImage: + description: The OneAgent image that is used to inject into + Pods. + type: string + dnsPolicy: + description: Set the DNS Policy for OneAgent pods. For details, + see Pods DNS Policy (https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy). + type: string + env: + description: Set additional environment variables for the + OneAgent pods. + 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: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + 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: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + 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: Use a custom OneAgent Docker image. Defaults + to the image from the Dynatrace cluster. + type: string + initResources: + description: |- + Define resources requests and limits for the initContainer. For details, see Managing resources for containers + (https://kubernetes.io/docs/concepts/configuration/manage-resources-containers). + 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 + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + 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 + labels: + additionalProperties: + type: string + description: Your defined labels for OneAgent pods in order + to structure workloads as desired. + type: object + nodeSelector: + additionalProperties: + type: string + description: Specify the node selector that controls on which + nodes OneAgent will be deployed. + type: object + oneAgentResources: + description: |- + Resource settings for OneAgent container. Consumption of the OneAgent heavily depends on the workload to monitor. You can use the default settings in the CR. + Note: resource.requests shows the values needed to run; resource.limits shows the maximum limits for the pod. + 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 + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + 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 + priorityClassName: + description: |- + Assign a priority class to the OneAgent pods. By default, no class is set. + For details, see Pod Priority and Preemption (https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/). + type: string + tolerations: + description: Tolerations to include with the OneAgent DaemonSet. + For details, see Taints and Tolerations (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 + version: + description: The OneAgent version to be used. + type: string + type: object + hostGroup: + description: Sets a host group for OneAgent. + type: string + hostMonitoring: + description: |- + Has a single OneAgent per node via DaemonSet. + Doesn't inject into application pods. + nullable: true + properties: + annotations: + additionalProperties: + type: string + description: Add custom OneAgent annotations. + type: object + args: + description: |- + Set additional arguments to the OneAgent installer. + For available options, see Linux custom installation (https://www.dynatrace.com/support/help/setup-and-configuration/dynatrace-oneagent/installation-and-operation/linux/installation/customize-oneagent-installation-on-linux). + For the list of limitations, see Limitations (https://www.dynatrace.com/support/help/setup-and-configuration/setup-on-container-platforms/docker/set-up-dynatrace-oneagent-as-docker-container#limitations). + items: + type: string + type: array + x-kubernetes-list-type: set + autoUpdate: + description: |- + Disables automatic restarts of OneAgent pods in case a new version is available (https://www.dynatrace.com/support/help/setup-and-configuration/setup-on-container-platforms/kubernetes/get-started-with-kubernetes-monitoring#disable-auto). + Enabled by default. + type: boolean + dnsPolicy: + description: Set the DNS Policy for OneAgent pods. For details, + see Pods DNS Policy (https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy). + type: string + env: + description: Set additional environment variables for the + OneAgent pods. + 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: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + 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: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + 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: Use a custom OneAgent Docker image. Defaults + to the image from the Dynatrace cluster. + type: string + labels: + additionalProperties: + type: string + description: Your defined labels for OneAgent pods in order + to structure workloads as desired. + type: object + nodeSelector: + additionalProperties: + type: string + description: Specify the node selector that controls on which + nodes OneAgent will be deployed. + type: object + oneAgentResources: + description: |- + Resource settings for OneAgent container. Consumption of the OneAgent heavily depends on the workload to monitor. You can use the default settings in the CR. + Note: resource.requests shows the values needed to run; resource.limits shows the maximum limits for the pod. + 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 + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + 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 + priorityClassName: + description: |- + Assign a priority class to the OneAgent pods. By default, no class is set. + For details, see Pod Priority and Preemption (https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/). + type: string + tolerations: + description: Tolerations to include with the OneAgent DaemonSet. + For details, see Taints and Tolerations (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 + version: + description: The OneAgent version to be used. + type: string + type: object + type: object + proxy: + description: |- + Set custom proxy settings either directly or from a secret with the field proxy. + Note: Applies to Dynatrace Operator, ActiveGate, and OneAgents. + properties: + value: + description: Proxy URL. It has preference over ValueFrom. + nullable: true + type: string + valueFrom: + description: Secret containing proxy URL. + nullable: true + type: string + type: object + routing: + description: Configuration for Routing + properties: + customProperties: + description: |- + Add a custom properties file by providing it as a value or reference it from a secret + If referenced from a secret, make sure the key is called 'customProperties' + properties: + value: + description: Custom properties value. + nullable: true + type: string + valueFrom: + description: Custom properties secret. + nullable: true + type: string + type: object + enabled: + description: Enables Capability + type: boolean + env: + description: List of environment variables to set for the ActiveGate + 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: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + 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: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + 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 + group: + description: Set activation group for ActiveGate + type: string + image: + description: The ActiveGate container image. Defaults to the latest + ActiveGate image provided by the registry on the tenant + type: string + labels: + additionalProperties: + type: string + description: Adds additional labels for the ActiveGate pods + type: object + nodeSelector: + additionalProperties: + type: string + description: Node selector to control the selection of nodes + type: object + replicas: + description: Amount of replicas for your ActiveGates + format: int32 + type: integer + resources: + description: Define resources requests and limits for single ActiveGate + pods + 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 + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + 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 + tolerations: + description: Set tolerations for the ActiveGate pods + 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: Adds TopologySpreadConstraints for the ActiveGate + pods + 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 + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select 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. + 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. + 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. + 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. + 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. + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + type: object + skipCertCheck: + description: |- + Disable certificate check for the connection between Dynatrace Operator and the Dynatrace Cluster. + Set to true if you want to skip certification validation checks. + type: boolean + tokens: + description: Name of the secret holding the tokens used for connecting + to Dynatrace. + type: string + trustedCAs: + description: |- + Adds custom RootCAs from a configmap. Put the certificate under certs within your configmap. + Note: Applies to Dynatrace Operator, OneAgent and ActiveGate. + type: string + required: + - apiUrl + type: object + status: + description: DynaKubeStatus defines the observed state of DynaKube + properties: + activeGate: + description: Observed state of ActiveGate + properties: + connectionInfoStatus: + description: Information about Active Gate's connections + properties: + endpoints: + description: Available connection endpoints + type: string + lastRequest: + description: Time of the last connection request + format: date-time + type: string + tenantUUID: + description: UUID of the tenant, received from the tenant + type: string + type: object + imageID: + description: Image ID + type: string + lastProbeTimestamp: + description: Indicates when the last check for a new version was + performed + format: date-time + type: string + serviceIPs: + description: The ClusterIPs set by Kubernetes on the ActiveGate + Service created by the Operator + items: + type: string + type: array + source: + description: Source of the image (tenant-registry, public-registry, + ...) + type: string + type: + description: Image type + type: string + version: + description: Image version + type: string + type: object + codeModules: + description: Observed state of Code Modules + properties: + imageID: + description: Image ID + type: string + lastProbeTimestamp: + description: Indicates when the last check for a new version was + performed + format: date-time + type: string + source: + description: Source of the image (tenant-registry, public-registry, + ...) + type: string + type: + description: Image type + type: string + version: + description: Image version + type: string + type: object + conditions: + description: Conditions includes status about the current state of + the instance + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + 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. + 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 + dynatraceApi: + description: Observed state of Dynatrace API + properties: + lastTokenScopeRequest: + description: Time of the last token request + format: date-time + type: string + type: object + kubeSystemUUID: + description: KubeSystemUUID contains the UUID of the current Kubernetes + cluster + type: string + lastTokenProbeTimestamp: + description: LastTokenProbeTimestamp tracks when the last request + for the API token validity was sent + format: date-time + type: string + oneAgent: + description: Observed state of OneAgent + properties: + connectionInfoStatus: + description: Information about OneAgent's connections + properties: + communicationHosts: + description: List of communication hosts + items: + properties: + host: + description: Host domain + type: string + port: + description: Connection port + format: int32 + type: integer + protocol: + description: Connection protocol + type: string + type: object + type: array + endpoints: + description: Available connection endpoints + type: string + lastRequest: + description: Time of the last connection request + format: date-time + type: string + tenantUUID: + description: UUID of the tenant, received from the tenant + type: string + type: object + healthcheck: + description: Commands used for OneAgent's readiness probe + type: object + x-kubernetes-preserve-unknown-fields: true + imageID: + description: Image ID + type: string + instances: + additionalProperties: + properties: + ipAddress: + description: IP address of the pod + type: string + podName: + description: Name of the OneAgent pod + type: string + type: object + description: List of deployed OneAgent instances + type: object + lastInstanceStatusUpdate: + description: Time of the last instance status update + format: date-time + type: string + lastProbeTimestamp: + description: Indicates when the last check for a new version was + performed + format: date-time + type: string + source: + description: Source of the image (tenant-registry, public-registry, + ...) + type: string + type: + description: Image type + type: string + version: + description: Image version + type: string + type: object + phase: + description: Defines the current state (Running, Updating, Error, + ...) + type: string + updatedTimestamp: + description: UpdatedTimestamp indicates when the instance was last + updated + format: date-time + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.apiUrl + name: ApiUrl + type: string + - jsonPath: .status.phase + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta2 + schema: + openAPIV3Schema: + description: DynaKube is the Schema for the DynaKube 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: DynaKubeSpec defines the desired state of DynaKube + properties: + activeGate: + description: General configuration about ActiveGate instances. + properties: + annotations: + additionalProperties: + type: string + description: Adds additional annotations to the ActiveGate pods + type: object + capabilities: + description: "Defines the ActiveGate pod capabilities\nPossible + values:\n\t- `routing` enables OneAgent routing.\n\t- `kubernetes-monitoring` + enables Kubernetes API monitoring.\n\t- `metrics-ingest` opens + the metrics ingest endpoint on the DynaKube ActiveGate and redirects + all pods to it.\n\t- `dynatrace-api` enables calling the Dynatrace + API via ActiveGate." + items: + type: string + type: array + customProperties: + description: |- + Add a custom properties file by providing it as a value or reference it from a secret + If referenced from a secret, make sure the key is called `customProperties` + properties: + value: + description: Custom properties value. + nullable: true + type: string + valueFrom: + description: Custom properties secret. + nullable: true + type: string + type: object + dnsPolicy: + description: Sets DNS Policy for the ActiveGate pods + type: string + env: + description: List of environment variables to set for the ActiveGate + 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: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + 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: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + 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 + group: + description: Set activation group for ActiveGate + type: string + image: + description: Use a custom ActiveGate image. Defaults to the latest + ActiveGate image provided by the registry on the tenant + type: string + labels: + additionalProperties: + type: string + description: Your defined labels for ActiveGate pods in order + to structure workloads as desired. + type: object + nodeSelector: + additionalProperties: + type: string + description: Specify the node selector that controls on which + nodes ActiveGate will be deployed. + type: object + priorityClassName: + description: |- + Assign a priority class to the ActiveGate pods. By default, no class is set. + For details, see Pod Priority and Preemption. (https://dt-url.net/n8437bl) + type: string + replicas: + default: 1 + description: Amount of replicas for your ActiveGates + format: int32 + type: integer + resources: + description: |- + Resource settings for ActiveGate container. + Consumption of the ActiveGate heavily depends on the workload to monitor. Adjust values accordingly. + 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 + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + 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 + tlsSecretName: + description: |- + The name of a secret containing ActiveGate TLS cert+key and password. If not set, self-signed certificate is used. + `server.p12`: certificate+key pair in pkcs12 format + `password`: passphrase to read server.p12 + type: string + tolerations: + description: Set tolerations for the ActiveGate pods + 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: Adds TopologySpreadConstraints to the ActiveGate + pods + 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 + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select 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. + 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. + 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. + 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. + 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. + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + type: object + apiUrl: + description: |- + Dynatrace `apiUrl`, including the `/api` path at the end. + - For SaaS, set `YOUR_ENVIRONMENT_ID` to your environment ID. + - For Managed, change the `apiUrl` address. + For instructions on how to determine the environment ID and how to configure the apiUrl address, see Environment ID (https://www.dynatrace.com/support/help/get-started/monitoring-environment/environment-id). + type: string + customPullSecret: + description: |- + Defines a custom pull secret in case you use a private registry when pulling images from the Dynatrace environment. + To define a custom pull secret and learn about the expected behavior, see Configure customPullSecret + (https://www.dynatrace.com/support/help/setup-and-configuration/setup-on-container-platforms/kubernetes/get-started-with-kubernetes-monitoring/dto-config-options-k8s#custompullsecret). + type: string + dynatraceApiRequestThreshold: + default: 15 + description: Minimum minutes between Dynatrace API requests. + type: integer + enableIstio: + description: |- + When enabled, and if Istio is installed in the Kubernetes environment, Dynatrace Operator will create the corresponding VirtualService and ServiceEntry objects to allow access to the Dynatrace Cluster from the OneAgent or ActiveGate. + Disabled by default. + type: boolean + metadataEnrichment: + description: Configuration for Metadata Enrichment. + properties: + enabled: + default: false + description: Enables MetadataEnrichment, `false` by default. + type: boolean + namespaceSelector: + description: The namespaces where you want Dynatrace Operator + to inject enrichment. + 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 + required: + - enabled + type: object + networkZone: + description: Sets a network zone for the OneAgent and ActiveGate pods. + type: string + oneAgent: + description: |- + General configuration about OneAgent instances. + You can't enable more than one module (classicFullStack, cloudNativeFullStack, hostMonitoring, or applicationMonitoring). + properties: + applicationMonitoring: + description: |- + dynatrace-webhook injects into application pods based on labeled namespaces. + Has an optional CSI driver per node via DaemonSet to provide binaries to pods. + nullable: true + properties: + codeModulesImage: + description: The OneAgent image that is used to inject into + Pods. + type: string + initResources: + description: |- + Define resources requests and limits for the initContainer. For details, see Managing resources for containers + (https://kubernetes.io/docs/concepts/configuration/manage-resources-containers). + 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 + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + 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 + namespaceSelector: + description: |- + Applicable only for applicationMonitoring or cloudNativeFullStack configuration types. The namespaces where you want Dynatrace Operator to inject. + For more information, see Configure monitoring for namespaces and pods (https://www.dynatrace.com/support/help/setup-and-configuration/setup-on-container-platforms/kubernetes/get-started-with-kubernetes-monitoring/dto-config-options-k8s#annotate). + 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 + useCSIDriver: + default: false + description: Set if you want to use the CSIDriver. Don't enable + it if you do not have access to Kubernetes nodes or if you + lack privileges. + type: boolean + version: + description: The OneAgent version to be used. + type: string + type: object + classicFullStack: + description: |- + Has a single OneAgent per node via DaemonSet. + Injection is performed via the same OneAgent DaemonSet. + nullable: true + properties: + annotations: + additionalProperties: + type: string + description: Add custom OneAgent annotations. + type: object + args: + description: |- + Set additional arguments to the OneAgent installer. + For available options, see Linux custom installation (https://www.dynatrace.com/support/help/setup-and-configuration/dynatrace-oneagent/installation-and-operation/linux/installation/customize-oneagent-installation-on-linux). + For the list of limitations, see Limitations (https://www.dynatrace.com/support/help/setup-and-configuration/setup-on-container-platforms/docker/set-up-dynatrace-oneagent-as-docker-container#limitations). + items: + type: string + type: array + x-kubernetes-list-type: set + autoUpdate: + default: true + description: |- + Disables automatic restarts of OneAgent pods in case a new version is available (https://www.dynatrace.com/support/help/setup-and-configuration/setup-on-container-platforms/kubernetes/get-started-with-kubernetes-monitoring#disable-auto). + Enabled by default. + type: boolean + dnsPolicy: + description: Set the DNS Policy for OneAgent pods. For details, + see Pods DNS Policy (https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy). + type: string + env: + description: Set additional environment variables for the + OneAgent pods. + 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: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + 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: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + 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: Use a custom OneAgent Docker image. Defaults + to the image from the Dynatrace cluster. + type: string + labels: + additionalProperties: + type: string + description: Your defined labels for OneAgent pods in order + to structure workloads as desired. + type: object + nodeSelector: + additionalProperties: + type: string + description: Specify the node selector that controls on which + nodes OneAgent will be deployed. + type: object + oneAgentResources: + description: |- + Resource settings for OneAgent container. Consumption of the OneAgent heavily depends on the workload to monitor. You can use the default settings in the CR. + - `resource.requests` shows the values needed to run + - `resource.limits` shows the maximum limits for the pod + 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 + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + 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 + priorityClassName: + description: |- + Assign a priority class to the OneAgent pods. By default, no class is set. + For details, see Pod Priority and Preemption (https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/). + type: string + secCompProfile: + description: The SecComp Profile that will be configured in + order to run in secure computing mode. + type: string + tolerations: + description: Tolerations to include with the OneAgent DaemonSet. + For details, see Taints and Tolerations (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 + version: + description: The OneAgent version to be used for OneAgents + running in the dedicated pod. This setting doesn't affect + the OneAgent version used for application monitoring. + type: string + type: object + cloudNativeFullStack: + description: |- + Has a single OneAgent per node via DaemonSet. + dynatrace-webhook injects into application pods based on labeled namespaces. + Has a CSI driver per node via DaemonSet to provide binaries to pods. + nullable: true + properties: + annotations: + additionalProperties: + type: string + description: Add custom OneAgent annotations. + type: object + args: + description: |- + Set additional arguments to the OneAgent installer. + For available options, see Linux custom installation (https://www.dynatrace.com/support/help/setup-and-configuration/dynatrace-oneagent/installation-and-operation/linux/installation/customize-oneagent-installation-on-linux). + For the list of limitations, see Limitations (https://www.dynatrace.com/support/help/setup-and-configuration/setup-on-container-platforms/docker/set-up-dynatrace-oneagent-as-docker-container#limitations). + items: + type: string + type: array + x-kubernetes-list-type: set + autoUpdate: + default: true + description: |- + Disables automatic restarts of OneAgent pods in case a new version is available (https://www.dynatrace.com/support/help/setup-and-configuration/setup-on-container-platforms/kubernetes/get-started-with-kubernetes-monitoring#disable-auto). + Enabled by default. + type: boolean + codeModulesImage: + description: The OneAgent image that is used to inject into + Pods. + type: string + dnsPolicy: + description: Set the DNS Policy for OneAgent pods. For details, + see Pods DNS Policy (https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy). + type: string + env: + description: Set additional environment variables for the + OneAgent pods. + 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: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + 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: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + 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: Use a custom OneAgent Docker image. Defaults + to the image from the Dynatrace cluster. + type: string + initResources: + description: |- + Define resources requests and limits for the initContainer. For details, see Managing resources for containers + (https://kubernetes.io/docs/concepts/configuration/manage-resources-containers). + 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 + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + 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 + labels: + additionalProperties: + type: string + description: Your defined labels for OneAgent pods in order + to structure workloads as desired. + type: object + namespaceSelector: + description: |- + Applicable only for applicationMonitoring or cloudNativeFullStack configuration types. The namespaces where you want Dynatrace Operator to inject. + For more information, see Configure monitoring for namespaces and pods (https://www.dynatrace.com/support/help/setup-and-configuration/setup-on-container-platforms/kubernetes/get-started-with-kubernetes-monitoring/dto-config-options-k8s#annotate). + 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 + nodeSelector: + additionalProperties: + type: string + description: Specify the node selector that controls on which + nodes OneAgent will be deployed. + type: object + oneAgentResources: + description: |- + Resource settings for OneAgent container. Consumption of the OneAgent heavily depends on the workload to monitor. You can use the default settings in the CR. + - `resource.requests` shows the values needed to run + - `resource.limits` shows the maximum limits for the pod + 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 + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + 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 + priorityClassName: + description: |- + Assign a priority class to the OneAgent pods. By default, no class is set. + For details, see Pod Priority and Preemption (https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/). + type: string + secCompProfile: + description: The SecComp Profile that will be configured in + order to run in secure computing mode. + type: string + tolerations: + description: Tolerations to include with the OneAgent DaemonSet. + For details, see Taints and Tolerations (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 + version: + description: The OneAgent version to be used for OneAgents + running in the dedicated pod. This setting doesn't affect + the OneAgent version used for application monitoring. + type: string + type: object + hostGroup: + description: |- + Specify the name of the group to which you want to assign the host. + This method is preferred over the now obsolete `--set-host-group` argument. + If both settings are used, this field takes precedence over the `--set-host-group` argument. + type: string + hostMonitoring: + description: |- + Has a single OneAgent per node via DaemonSet. + Doesn't inject into application pods. + nullable: true + properties: + annotations: + additionalProperties: + type: string + description: Add custom OneAgent annotations. + type: object + args: + description: |- + Set additional arguments to the OneAgent installer. + For available options, see Linux custom installation (https://www.dynatrace.com/support/help/setup-and-configuration/dynatrace-oneagent/installation-and-operation/linux/installation/customize-oneagent-installation-on-linux). + For the list of limitations, see Limitations (https://www.dynatrace.com/support/help/setup-and-configuration/setup-on-container-platforms/docker/set-up-dynatrace-oneagent-as-docker-container#limitations). + items: + type: string + type: array + x-kubernetes-list-type: set + autoUpdate: + default: true + description: |- + Disables automatic restarts of OneAgent pods in case a new version is available (https://www.dynatrace.com/support/help/setup-and-configuration/setup-on-container-platforms/kubernetes/get-started-with-kubernetes-monitoring#disable-auto). + Enabled by default. + type: boolean + dnsPolicy: + description: Set the DNS Policy for OneAgent pods. For details, + see Pods DNS Policy (https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy). + type: string + env: + description: Set additional environment variables for the + OneAgent pods. + 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: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + 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: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + 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: Use a custom OneAgent Docker image. Defaults + to the image from the Dynatrace cluster. + type: string + labels: + additionalProperties: + type: string + description: Your defined labels for OneAgent pods in order + to structure workloads as desired. + type: object + nodeSelector: + additionalProperties: + type: string + description: Specify the node selector that controls on which + nodes OneAgent will be deployed. + type: object + oneAgentResources: + description: |- + Resource settings for OneAgent container. Consumption of the OneAgent heavily depends on the workload to monitor. You can use the default settings in the CR. + - `resource.requests` shows the values needed to run + - `resource.limits` shows the maximum limits for the pod + 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 + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + 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 + priorityClassName: + description: |- + Assign a priority class to the OneAgent pods. By default, no class is set. + For details, see Pod Priority and Preemption (https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/). + type: string + secCompProfile: + description: The SecComp Profile that will be configured in + order to run in secure computing mode. + type: string + tolerations: + description: Tolerations to include with the OneAgent DaemonSet. + For details, see Taints and Tolerations (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 + version: + description: The OneAgent version to be used for OneAgents + running in the dedicated pod. This setting doesn't affect + the OneAgent version used for application monitoring. + type: string + type: object + type: object + proxy: + description: |- + Set custom proxy settings either directly or from a secret with the field `proxy`. + Applies to Dynatrace Operator, ActiveGate, and OneAgents. + properties: + value: + description: Proxy URL. It has preference over ValueFrom. + nullable: true + type: string + valueFrom: + description: Secret containing proxy URL. + nullable: true + type: string + type: object + skipCertCheck: + description: |- + Disable certificate check for the connection between Dynatrace Operator and the Dynatrace Cluster. + Set to `true` if you want to skip certification validation checks. + type: boolean + tokens: + description: Name of the secret holding the tokens used for connecting + to Dynatrace. + type: string + trustedCAs: + description: |- + Adds custom RootCAs from a configmap. + The key to the data must be `certs`. + This applies to both the Dynatrace Operator and the OneAgent. Doesn't apply to ActiveGate. + type: string + required: + - apiUrl + type: object + status: + description: DynaKubeStatus defines the observed state of DynaKube + properties: + activeGate: + description: Observed state of ActiveGate + properties: + connectionInfoStatus: + description: Information about Active Gate's connections + properties: + endpoints: + description: Available connection endpoints + type: string + lastRequest: + description: Time of the last connection request + format: date-time + type: string + tenantUUID: + description: UUID of the tenant, received from the tenant + type: string + type: object + imageID: + description: Image ID + type: string + lastProbeTimestamp: + description: Indicates when the last check for a new version was + performed + format: date-time + type: string + serviceIPs: + description: The ClusterIPs set by Kubernetes on the ActiveGate + Service created by the Operator + items: + type: string + type: array + source: + description: Source of the image (tenant-registry, public-registry, + ...) + type: string + type: + description: Image type + type: string + version: + description: Image version + type: string + type: object + codeModules: + description: Observed state of Code Modules + properties: + imageID: + description: Image ID + type: string + lastProbeTimestamp: + description: Indicates when the last check for a new version was + performed + format: date-time + type: string + source: + description: Source of the image (tenant-registry, public-registry, + ...) + type: string + type: + description: Image type + type: string + version: + description: Image version + type: string + type: object + conditions: + description: Conditions includes status about the current state of + the instance + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + 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. + 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 + dynatraceApi: + description: Observed state of Dynatrace API + properties: + lastTokenScopeRequest: + description: Time of the last token request + format: date-time + type: string + type: object + kubeSystemUUID: + description: KubeSystemUUID contains the UUID of the current Kubernetes + cluster + type: string + kubernetesClusterMEID: + description: KubernetesClusterMEID contains the ID of the monitored + entity that points to the Kubernetes cluster + type: string + kubernetesClusterName: + description: KubernetesClusterName contains the display name (also + know as label) of the monitored entity that points to the Kubernetes + cluster + type: string + metadataEnrichment: + description: Observed state of Metadata-Enrichment + properties: + rules: + items: + properties: + enabled: + type: boolean + source: + type: string + target: + type: string + type: + type: string + type: object + type: array + type: object + oneAgent: + description: Observed state of OneAgent + properties: + connectionInfoStatus: + description: Information about OneAgent's connections + properties: + communicationHosts: + description: List of communication hosts + items: + properties: + host: + description: Host domain + type: string + port: + description: Connection port + format: int32 + type: integer + protocol: + description: Connection protocol + type: string + type: object + type: array + endpoints: + description: Available connection endpoints + type: string + lastRequest: + description: Time of the last connection request + format: date-time + type: string + tenantUUID: + description: UUID of the tenant, received from the tenant + type: string + type: object + healthcheck: + description: Commands used for OneAgent's readiness probe + type: object + x-kubernetes-preserve-unknown-fields: true + imageID: + description: Image ID + type: string + instances: + additionalProperties: + properties: + ipAddress: + description: IP address of the pod + type: string + podName: + description: Name of the OneAgent pod + type: string + type: object + description: List of deployed OneAgent instances + type: object + lastInstanceStatusUpdate: + description: Time of the last instance status update + format: date-time + type: string + lastProbeTimestamp: + description: Indicates when the last check for a new version was + performed + format: date-time + type: string + source: + description: Source of the image (tenant-registry, public-registry, + ...) + type: string + type: + description: Image type + type: string + version: + description: Image version + type: string + type: object + phase: + description: Defines the current state (Running, Updating, Error, + ...) + type: string + updatedTimestamp: + description: UpdatedTimestamp indicates when the instance was last + updated + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + name: edgeconnects.dynatrace.com +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + name: dynatrace-webhook + namespace: {{.Release.Namespace}} + path: /convert + conversionReviewVersions: + - v1 + - v1beta1 + group: dynatrace.com + names: + categories: + - dynatrace + kind: EdgeConnect + listKind: EdgeConnectList + plural: edgeconnects + shortNames: + - ec + - ecs + singular: edgeconnect + preserveUnknownFields: false + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.apiServer + name: ApiServer + type: string + - jsonPath: .status.phase + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: EdgeConnect is the Schema for the EdgeConnect 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: EdgeConnectSpec defines the desired state of EdgeConnect. + properties: + annotations: + additionalProperties: + type: string + description: Adds additional annotations to the EdgeConnect pods + type: object + apiServer: + description: Location of the Dynatrace API to connect to, including + your specific environment UUID + type: string + autoUpdate: + default: true + description: 'Enables automatic restarts of EdgeConnect pods in case + a new version is available (the default value is: true)' + type: boolean + caCertsRef: + description: Adds custom root certificate from a configmap. Put the + certificate under certs within your configmap. + type: string + customPullSecret: + description: Pull secret for your private registry + type: string + env: + description: Adds additional environment variables to the EdgeConnect + pods + 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: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + 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: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + 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 + hostPatterns: + description: Host patterns to be set in the tenant, only considered + when provisioning is enabled. + items: + type: string + type: array + hostRestrictions: + description: Restrict outgoing HTTP requests to your internal resources + to specified hosts + example: internal.example.org,*.dev.example.org + type: string + imageRef: + description: Overrides the default image + properties: + repository: + description: Custom EdgeConnect image repository + example: docker.io/dynatrace/edgeconnect + type: string + tag: + description: Indicates version of the EdgeConnect image to use + type: string + type: object + kubernetesAutomation: + description: KubernetesAutomation enables Kubernetes Automation for + Workflows + properties: + enabled: + description: Enables Kubernetes Automation for Workflows + type: boolean + type: object + labels: + additionalProperties: + type: string + description: Adds additional labels to the EdgeConnect pods + type: object + nodeSelector: + additionalProperties: + type: string + description: Node selector to control the selection of nodes for the + EdgeConnect pods + type: object + oauth: + description: EdgeConnect uses the OAuth client to authenticate itself + with the Dynatrace platform. + properties: + clientSecret: + description: Name of the secret that holds oauth clientId/secret + type: string + endpoint: + description: Token endpoint URL of Dynatrace SSO + type: string + provisioner: + description: Determines if the operator will create the EdgeConnect + and light OAuth client on the cluster using the credentials + provided. Requires more scopes than default behavior. + type: boolean + resource: + description: URN identifying your account. You get the URN when + creating the OAuth client + type: string + required: + - clientSecret + - endpoint + - resource + type: object + proxy: + description: General configurations for proxy settings. + properties: + authRef: + description: |- + Secret name which contains the username and password used for authentication with the proxy, using the + "Basic" HTTP authentication scheme. + type: string + host: + description: Server address (hostname or IP address) of the proxy. + type: string + noProxy: + description: |- + NoProxy represents the NO_PROXY or no_proxy environment + variable. It specifies a string that contains comma-separated values + specifying hosts that should be excluded from proxying. + type: string + port: + description: Port of the proxy. + format: int32 + type: integer + type: object + replicas: + default: 1 + description: 'Amount of replicas for your EdgeConnect (the default + value is: 1)' + format: int32 + type: integer + resources: + description: Defines resources requests and limits for single pods + 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 + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + 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 + serviceAccountName: + default: dynatrace-edgeconnect + description: ServiceAccountName that allows EdgeConnect to access + the Kubernetes API + type: string + tolerations: + description: Sets tolerations for the EdgeConnect pods + 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: Sets topology spread constraints for the EdgeConnect + pods + 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 + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select 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. + 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. + 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. + 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. + 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. + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + required: + - apiServer + - oauth + type: object + status: + description: EdgeConnectStatus defines the observed state of EdgeConnect. + properties: + conditions: + description: Conditions includes status about the current state of + the instance + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + 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. + 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 + kubeSystemUID: + description: kube-system namespace uid + type: string + phase: + description: Defines the current state (Running, Updating, Error, + ...) + type: string + updatedTimestamp: + description: Indicates when the resource was last updated + format: date-time + type: string + version: + description: Version used for the Edgeconnect image + properties: + imageID: + description: Image ID + type: string + lastProbeTimestamp: + description: Indicates when the last check for a new version was + performed + format: date-time + type: string + source: + description: Source of the image (tenant-registry, public-registry, + ...) + type: string + type: + description: Image type + type: string + version: + description: Image version + type: string + type: object + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.apiServer + name: ApiServer + type: string + - jsonPath: .status.phase + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha2 + schema: + openAPIV3Schema: + description: EdgeConnect is the Schema for the EdgeConnect 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: EdgeConnectSpec defines the desired state of EdgeConnect. + properties: + annotations: + additionalProperties: + type: string + description: Adds additional annotations to the EdgeConnect pods + type: object + apiServer: + description: Location of the Dynatrace API to connect to, including + your specific environment UUID + type: string + autoUpdate: + default: true + description: 'Enables automatic restarts of EdgeConnect pods in case + a new version is available (the default value is: true)' + type: boolean + caCertsRef: + description: Adds custom root certificate from a configmap. Put the + certificate under certs within your configmap. + type: string + customPullSecret: + description: Pull secret for your private registry + type: string + env: + description: Adds additional environment variables to the EdgeConnect + pods + 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: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + 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: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + 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 + hostPatterns: + description: Host patterns to be set in the tenant, only considered + when provisioning is enabled. + items: + type: string + type: array + hostRestrictions: + description: Restrict outgoing HTTP requests to your internal resources + to specified hosts + example: internal.example.org,*.dev.example.org + items: + type: string + type: array + imageRef: + description: Overrides the default image + properties: + repository: + description: Custom EdgeConnect image repository + example: docker.io/dynatrace/edgeconnect + type: string + tag: + description: Indicates version of the EdgeConnect image to use + type: string + type: object + kubernetesAutomation: + description: KubernetesAutomation enables Kubernetes Automation for + Workflows + properties: + enabled: + description: Enables Kubernetes Automation for Workflows + type: boolean + type: object + labels: + additionalProperties: + type: string + description: Adds additional labels to the EdgeConnect pods + type: object + nodeSelector: + additionalProperties: + type: string + description: Node selector to control the selection of nodes for the + EdgeConnect pods + type: object + oauth: + description: EdgeConnect uses the OAuth client to authenticate itself + with the Dynatrace platform. + properties: + clientSecret: + description: Name of the secret that holds oauth clientId/secret + type: string + endpoint: + description: Token endpoint URL of Dynatrace SSO + type: string + provisioner: + description: Determines if the operator will create the EdgeConnect + and light OAuth client on the cluster using the credentials + provided. Requires more scopes than default behavior. + type: boolean + resource: + description: URN identifying your account. You get the URN when + creating the OAuth client + type: string + required: + - clientSecret + - endpoint + - resource + type: object + proxy: + description: General configurations for proxy settings. + properties: + authRef: + description: |- + Secret name which contains the username and password used for authentication with the proxy, using the + "Basic" HTTP authentication scheme. + type: string + host: + description: Server address (hostname or IP address) of the proxy. + type: string + noProxy: + description: |- + NoProxy represents the NO_PROXY or no_proxy environment + variable. It specifies a string that contains comma-separated values + specifying hosts that should be excluded from proxying. + type: string + port: + description: Port of the proxy. + format: int32 + type: integer + type: object + replicas: + default: 1 + description: 'Amount of replicas for your EdgeConnect (the default + value is: 1)' + format: int32 + type: integer + resources: + description: Defines resources requests and limits for single pods + 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 + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + 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 + serviceAccountName: + default: dynatrace-edgeconnect + description: ServiceAccountName that allows EdgeConnect to access + the Kubernetes API + type: string + tolerations: + description: Sets tolerations for the EdgeConnect pods + 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: Sets topology spread constraints for the EdgeConnect + pods + 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 + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select 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. + 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. + 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. + 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. + 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. + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + required: + - apiServer + - oauth + type: object + status: + description: EdgeConnectStatus defines the observed state of EdgeConnect. + properties: + conditions: + description: Conditions includes status about the current state of + the instance + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + 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. + 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 + kubeSystemUID: + description: kube-system namespace uid + type: string + phase: + description: Defines the current state (Running, Updating, Error, + ...) + type: string + updatedTimestamp: + description: Indicates when the resource was last updated + format: date-time + type: string + version: + description: Version used for the Edgeconnect image + properties: + imageID: + description: Image ID + type: string + lastProbeTimestamp: + description: Indicates when the last check for a new version was + performed + format: date-time + type: string + source: + description: Source of the image (tenant-registry, public-registry, + ...) + type: string + type: + description: Image type + type: string + version: + description: Image version + type: string + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end -}} diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/csi/clusterrole-csi.yaml b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/csi/clusterrole-csi.yaml new file mode 100644 index 000000000..213bd73b0 --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/csi/clusterrole-csi.yaml @@ -0,0 +1,47 @@ +{{ if eq (include "dynatrace-operator.needCSI" .) "true" }} +# Copyright 2021 Dynatrace LLC + +# 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. +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: dynatrace-oneagent-csi-driver + labels: + {{- include "dynatrace-operator.csiLabels" . | nindent 4 }} +rules: + {{- if (eq (include "dynatrace-operator.platform" .) "openshift") }} + - apiGroups: + - security.openshift.io + resourceNames: + - privileged + resources: + - securitycontextconstraints + verbs: + - use + {{ end }} +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: dynatrace-oneagent-csi-driver + labels: + {{- include "dynatrace-operator.csiLabels" . | nindent 4 }} +subjects: + - kind: ServiceAccount + name: dynatrace-oneagent-csi-driver + namespace: {{ .Release.Namespace }} +roleRef: + kind: ClusterRole + name: dynatrace-oneagent-csi-driver + apiGroup: rbac.authorization.k8s.io +{{- end -}} diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/csi/csidriver.yaml b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/csi/csidriver.yaml new file mode 100644 index 000000000..dbef0127a --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/csi/csidriver.yaml @@ -0,0 +1,29 @@ +{{ if eq (include "dynatrace-operator.needCSI" .) "true" }} +# Copyright 2021 Dynatrace LLC + +# 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. +apiVersion: storage.k8s.io/v1 +kind: CSIDriver +metadata: + name: csi.oneagent.dynatrace.com + labels: + {{- if eq (include "dynatrace-operator.platform" .) "openshift" }} + security.openshift.io/csi-ephemeral-volume-profile: "restricted" + {{- end }} + {{- include "dynatrace-operator.csiLabels" . | nindent 4 }} +spec: + attachRequired: false + podInfoOnMount: true + volumeLifecycleModes: + - Ephemeral +{{- end -}} diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/csi/daemonset.yaml b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/csi/daemonset.yaml new file mode 100644 index 000000000..c24305ed4 --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/csi/daemonset.yaml @@ -0,0 +1,280 @@ +{{ if eq (include "dynatrace-operator.needCSI" .) "true" }} +# Copyright 2021 Dynatrace LLC + +# 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. +apiVersion: apps/v1 +kind: DaemonSet +metadata: + annotations: + {{- if .Values.csidriver.annotations }} + {{- toYaml .Values.csidriver.annotations | nindent 4 }} + {{- end }} + labels: + {{- include "dynatrace-operator.csiLabels" . | nindent 4 }} + {{- if .Values.csidriver.labels }} + {{- toYaml .Values.csidriver.labels | nindent 4 }} + {{- end}} + name: dynatrace-oneagent-csi-driver + namespace: {{ .Release.Namespace }} +spec: + revisionHistoryLimit: 10 + selector: + matchLabels: + {{- include "dynatrace-operator.csiSelectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + dynatrace.com/inject: "false" + kubectl.kubernetes.io/default-container: provisioner + cluster-autoscaler.kubernetes.io/enable-ds-eviction: "false" + {{- if and (eq (default false .Values.apparmor) true) (ne (include "dynatrace-operator.platform" .) "openshift") }} + container.apparmor.security.beta.kubernetes.io/csi-init: runtime/default + container.apparmor.security.beta.kubernetes.io/server: runtime/default + container.apparmor.security.beta.kubernetes.io/provisioner: runtime/default + container.apparmor.security.beta.kubernetes.io/registrar: runtime/default + container.apparmor.security.beta.kubernetes.io/liveness-probe: runtime/default + {{- end}} + {{- if .Values.csidriver.annotations }} + {{- toYaml .Values.csidriver.annotations | nindent 8 }} + {{- end }} + labels: + {{- include "dynatrace-operator.csiLabels" . | nindent 8 }} + {{- include "dynatrace-operator.csiSelectorLabels" . | nindent 8 }} + {{- if .Values.csidriver.labels }} + {{- toYaml .Values.csidriver.labels | nindent 8 }} + {{- end }} + spec: + initContainers: + - name: csi-init + image: {{ include "dynatrace-operator.image" . }} + imagePullPolicy: Always + args: + - csi-init + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + resources: + {{- if .Values.csidriver.csiInit.resources }} + {{- toYaml .Values.csidriver.csiInit.resources | nindent 10 }} + {{- end }} + securityContext: + {{- toYaml .Values.csidriver.csiInit.securityContext| nindent 10 }} + volumeMounts: + - mountPath: /data + name: data-dir + containers: + # Used to receive/execute gRPC requests (NodePublishVolume/NodeUnpublishVolume) from kubelet to mount/unmount volumes for a pod + # - Needs access to the csi socket, needs to read/write to it, needs root permissions to do so. + # - Needs access to the filesystem of pods on the node, and mount stuff to it,needs to read/write to it, needs root permissions to do so + # - Needs access to a dedicated folder on the node to persist data, needs to read/write to it. + - name: server + image: {{ include "dynatrace-operator.image" . }} + imagePullPolicy: Always + args: + - csi-server + - --endpoint=unix://csi/csi.sock + - --node-id=$(KUBE_NODE_NAME) + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: KUBE_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: healthz + scheme: HTTP + initialDelaySeconds: 5 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 1 + ports: + - containerPort: 9808 + name: healthz + - containerPort: 8080 + name: metrics + resources: + {{- if .Values.csidriver.server.resources }} + {{- toYaml .Values.csidriver.server.resources | nindent 10 }} + {{- end }} + securityContext: + {{- toYaml .Values.csidriver.server.securityContext | nindent 10 }} + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /csi + name: plugin-dir + - mountPath: {{ include "dynatrace-operator.CSIMountPointDir" . }} + mountPropagation: Bidirectional + name: mountpoint-dir + - mountPath: /data + name: data-dir + mountPropagation: Bidirectional + - name: tmp-dir + mountPath: /tmp + - name: provisioner + image: {{ include "dynatrace-operator.image" . }} + imagePullPolicy: Always + args: + - csi-provisioner + - --health-probe-bind-address=:10090 + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + {{- if .Values.csidriver.maxUnmountedVolumeAge }} + - name: MAX_UNMOUNTED_VOLUME_AGE + value: "{{ .Values.csidriver.maxUnmountedVolumeAge}}" + {{- end }} + {{- include "dynatrace-operator.startupProbe" . | nindent 8 }} + livenessProbe: + failureThreshold: 3 + httpGet: + path: /livez + port: livez + scheme: HTTP + initialDelaySeconds: 5 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 1 + ports: + - name: livez + containerPort: 10090 + - name: metrics + containerPort: 8090 + resources: + {{- if .Values.csidriver.provisioner.resources }} + {{- toYaml .Values.csidriver.provisioner.resources | nindent 10 }} + {{- end }} + securityContext: + {{- toYaml .Values.csidriver.provisioner.securityContext | nindent 10 }} + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /data + name: data-dir + mountPropagation: Bidirectional + - mountPath: /tmp + name: tmp-dir + + # Used to make a gRPC request (GetPluginInfo()) to the driver to get driver name and driver contain + # - Needs access to the csi socket, needs to read/write to it, needs root permissions to do so. + # Used for registering the driver with kubelet + # - Needs access to the registration socket, needs to read/write to it, needs root permissions to do so. + - name: registrar + image: {{ include "dynatrace-operator.image" . }} + imagePullPolicy: Always + env: + - name: DRIVER_REG_SOCK_PATH + value: {{ include "dynatrace-operator.CSISocketPath" . }} + args: + - --csi-address=/csi/csi.sock + - --kubelet-registration-path=$(DRIVER_REG_SOCK_PATH) + command: + - csi-node-driver-registrar + resources: + {{- if .Values.csidriver.registrar.resources }} + {{- toYaml .Values.csidriver.registrar.resources | nindent 10 }} + {{- end }} + securityContext: + {{- toYaml .Values.csidriver.registrar.securityContext | nindent 10 }} + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /csi + name: plugin-dir + - mountPath: /registration + name: registration-dir + - mountPath: {{ include "dynatrace-operator.CSIPluginDir" . }} + name: lockfile-dir + # Used to make a gRPC request (Probe()) to the driver to check if its running + # - Needs access to the csi socket, needs to read/write to it, needs root permissions to do so. + - name: liveness-probe + image: {{ include "dynatrace-operator.image" . }} + imagePullPolicy: Always + args: + - --csi-address=/csi/csi.sock + - --health-port=9808 + command: + - livenessprobe + resources: + {{- if .Values.csidriver.livenessprobe.resources }} + {{- toYaml .Values.csidriver.livenessprobe.resources | nindent 10 }} + {{- end }} + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + securityContext: + {{- toYaml .Values.csidriver.livenessprobe.securityContext| nindent 10 }} + volumeMounts: + - mountPath: /csi + name: plugin-dir + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + securityContext: {} + serviceAccountName: dynatrace-oneagent-csi-driver + terminationGracePeriodSeconds: 30 + priorityClassName: {{ include "dynatrace-operator.CSIPriorityClassName" . }} + volumes: + # This volume is where the registrar registers the plugin with kubelet + - name: registration-dir + hostPath: + path: {{ include "dynatrace-operator.CSIRegistrationDir" . }} + type: Directory + # This volume is where the socket for kubelet->driver communication is done + - name: plugin-dir + hostPath: + path: {{ include "dynatrace-operator.CSIPluginDir" . }} + type: DirectoryOrCreate + - name: data-dir + hostPath: + path: {{ include "dynatrace-operator.CSIDataDir" . }} + type: DirectoryOrCreate + # This volume is where the driver mounts volumes + - name: mountpoint-dir + hostPath: + path: {{ include "dynatrace-operator.CSIMountPointDir" . }} + type: DirectoryOrCreate + # Used by the registrar to create its lockfile + - name: lockfile-dir + emptyDir: {} + # A volume for the driver to write temporary files to + - name: tmp-dir + emptyDir: {} + {{- if .Values.customPullSecret }} + imagePullSecrets: + - name: {{ .Values.customPullSecret }} + {{- end }} + {{- if .Values.csidriver.nodeSelector }} + nodeSelector: {{- toYaml .Values.csidriver.nodeSelector | nindent 8 }} + {{- end }} + {{- include "dynatrace-operator.nodeAffinity" . | nindent 6 }} + tolerations: + {{- if .Values.csidriver.tolerations }} + {{- toYaml .Values.csidriver.tolerations | nindent 8 }} + {{- end }} + {{- include "dynatrace-operator.defaultTolerations" . | nindent 8 }} + - key: ToBeDeletedByClusterAutoscaler + operator: Exists + effect: NoSchedule + updateStrategy: + {{- toYaml .Values.csidriver.updateStrategy | nindent 4 }} +{{- end -}} diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/csi/priority-class.yaml b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/csi/priority-class.yaml new file mode 100644 index 000000000..c668ac4d6 --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/csi/priority-class.yaml @@ -0,0 +1,23 @@ +{{ if (eq (include "dynatrace-operator.needPriorityClass" .) "true") }} + +# Copyright 2021 Dynatrace LLC + +# 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. +kind: PriorityClass +apiVersion: scheduling.k8s.io/v1 +metadata: + name: dynatrace-high-priority +value: {{ default 1000000 (int (.Values.csidriver).priorityClassValue) }} +globalDefault: false +description: "This priority class is used for Dynatrace Components in order to make sure they are not evicted in favor of other pods" +{{- end -}} diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/csi/role-csi.yaml b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/csi/role-csi.yaml new file mode 100644 index 000000000..654ab04e1 --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/csi/role-csi.yaml @@ -0,0 +1,70 @@ +{{ if eq (include "dynatrace-operator.needCSI" .) "true" }} +# Copyright 2021 Dynatrace LLC + +# 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. +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: dynatrace-oneagent-csi-driver + namespace: {{ .Release.Namespace }} + labels: + {{- include "dynatrace-operator.csiLabels" . | nindent 4 }} +rules: + - apiGroups: + - dynatrace.com + resources: + - dynakubes + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - secrets + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: dynatrace-oneagent-csi-driver + namespace: {{ .Release.Namespace }} + labels: + {{- include "dynatrace-operator.csiLabels" . | nindent 4 }} +subjects: + - kind: ServiceAccount + name: dynatrace-oneagent-csi-driver + namespace: {{ .Release.Namespace }} +roleRef: + kind: Role + name: dynatrace-oneagent-csi-driver + apiGroup: rbac.authorization.k8s.io +{{- end -}} diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/csi/serviceaccount-csi.yaml b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/csi/serviceaccount-csi.yaml new file mode 100644 index 000000000..7485d2644 --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/csi/serviceaccount-csi.yaml @@ -0,0 +1,22 @@ +{{ if eq (include "dynatrace-operator.needCSI" .) "true" }} +# Copyright 2021 Dynatrace LLC + +# 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. +apiVersion: v1 +kind: ServiceAccount +metadata: + name: dynatrace-oneagent-csi-driver + namespace: {{ .Release.Namespace }} + labels: + {{- include "dynatrace-operator.csiLabels" . | nindent 4 }} +{{- end -}} diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/edge-connect/serviceaccount-operator.yaml b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/edge-connect/serviceaccount-operator.yaml new file mode 100644 index 000000000..45f1d9ac6 --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/edge-connect/serviceaccount-operator.yaml @@ -0,0 +1,20 @@ +# Copyright 2021 Dynatrace LLC + +# 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. +apiVersion: v1 +kind: ServiceAccount +metadata: + name: dynatrace-edgeconnect + namespace: {{ .Release.Namespace }} + labels: + {{- include "dynatrace-operator.operatorLabels" . | nindent 4 }} diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/kubernetes-monitoring/clusterrole-kubernetes-monitoring.yaml b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/kubernetes-monitoring/clusterrole-kubernetes-monitoring.yaml new file mode 100644 index 000000000..fab70e877 --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/kubernetes-monitoring/clusterrole-kubernetes-monitoring.yaml @@ -0,0 +1,114 @@ +# Copyright 2021 Dynatrace LLC + +# 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. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: dynatrace-kubernetes-monitoring + labels: + {{- include "dynatrace-operator.activegateLabels" . | nindent 4 }} +rules: + - apiGroups: + - "" + resources: + - nodes + - pods + - namespaces + - replicationcontrollers + - events + - resourcequotas + - pods/proxy + - nodes/proxy + - nodes/metrics + - services + verbs: + - list + - watch + - get + - apiGroups: + - batch + resources: + - jobs + - cronjobs + verbs: + - list + - watch + - get + - apiGroups: + - apps + resources: + - deployments + - replicasets + - statefulsets + - daemonsets + verbs: + - list + - watch + - get + - apiGroups: + - apps.openshift.io + resources: + - deploymentconfigs + verbs: + - list + - watch + - get + - apiGroups: + - config.openshift.io + resources: + - clusterversions + verbs: + - list + - watch + - get + - apiGroups: + - dynatrace.com + resources: + - dynakubes + verbs: + - list + - watch + - get + - nonResourceURLs: + - /metrics + - /version + - /readyz + - /livez + verbs: + - get + {{- if (eq (include "dynatrace-operator.openshiftOrOlm" .) "true") }} + - apiGroups: + - security.openshift.io + resourceNames: + - privileged + - nonroot-v2 + resources: + - securitycontextconstraints + verbs: + - use + {{ end }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: dynatrace-kubernetes-monitoring + labels: + {{- include "dynatrace-operator.activegateLabels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: dynatrace-kubernetes-monitoring +subjects: + - kind: ServiceAccount + name: dynatrace-kubernetes-monitoring + namespace: {{ .Release.Namespace }} diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/kubernetes-monitoring/serviceaccount-kubernetes-monitoring.yaml b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/kubernetes-monitoring/serviceaccount-kubernetes-monitoring.yaml new file mode 100644 index 000000000..bd1e9c443 --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/kubernetes-monitoring/serviceaccount-kubernetes-monitoring.yaml @@ -0,0 +1,20 @@ +# Copyright 2021 Dynatrace LLC + +# 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. +apiVersion: v1 +kind: ServiceAccount +metadata: + name: dynatrace-kubernetes-monitoring + namespace: {{ .Release.Namespace }} + labels: + {{- include "dynatrace-operator.activegateLabels" . | nindent 4 }} diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/oneagent/clusterrole-oneagent.yaml b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/oneagent/clusterrole-oneagent.yaml new file mode 100644 index 000000000..3e0616725 --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/oneagent/clusterrole-oneagent.yaml @@ -0,0 +1,45 @@ +{{- if (eq (include "dynatrace-operator.openshiftOrOlm" .) "true") }} +# Copyright 2021 Dynatrace LLC + +# 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. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: dynatrace-dynakube-oneagent + labels: + {{- include "dynatrace-operator.oneagentLabels" . | nindent 4 }} +rules: + - apiGroups: + - security.openshift.io + resourceNames: + - privileged + resources: + - securitycontextconstraints + verbs: + - use +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: dynatrace-dynakube-oneagent + labels: + {{- include "dynatrace-operator.oneagentLabels" . | nindent 4 }} +subjects: + - kind: ServiceAccount + name: dynatrace-dynakube-oneagent + namespace: {{ .Release.Namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: dynatrace-dynakube-oneagent +{{ end }} diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/oneagent/serviceaccount-oneagent.yaml b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/oneagent/serviceaccount-oneagent.yaml new file mode 100644 index 000000000..18d3a9ae2 --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/oneagent/serviceaccount-oneagent.yaml @@ -0,0 +1,21 @@ +# Copyright 2021 Dynatrace LLC + +# 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. +apiVersion: v1 +kind: ServiceAccount +metadata: + name: dynatrace-dynakube-oneagent + namespace: {{ .Release.Namespace }} + labels: + {{- include "dynatrace-operator.oneagentLabels" . | nindent 4 }} +automountServiceAccountToken: false diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/operator/clusterrole-operator.yaml b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/operator/clusterrole-operator.yaml new file mode 100644 index 000000000..a9098dd3d --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/operator/clusterrole-operator.yaml @@ -0,0 +1,109 @@ +# Copyright 2021 Dynatrace LLC + +# 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. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: dynatrace-operator + labels: + {{- include "dynatrace-operator.operatorLabels" . | nindent 4 }} +rules: + - apiGroups: + - "" + resources: + - nodes + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - namespaces + verbs: + - get + - list + - watch + - update + - apiGroups: + - "" + resources: + - secrets + verbs: + - create + - apiGroups: + - "" + resources: + - secrets + resourceNames: + - dynatrace-dynakube-config + - dynatrace-metadata-enrichment-endpoint + verbs: + - get + - update + - delete + - list + - apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + resourceNames: + - dynatrace-webhook + verbs: + - get + - update + - apiGroups: + - admissionregistration.k8s.io + resources: + - validatingwebhookconfigurations + resourceNames: + - dynatrace-webhook + verbs: + - get + - update + - apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + resourceNames: + - dynakubes.dynatrace.com + - edgeconnects.dynatrace.com + verbs: + - get + - update + {{- if (eq (include "dynatrace-operator.openshiftOrOlm" .) "true") }} + - apiGroups: + - security.openshift.io + resourceNames: + - privileged + - nonroot-v2 + resources: + - securitycontextconstraints + verbs: + - use + {{ end }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: dynatrace-operator + labels: + {{- include "dynatrace-operator.operatorLabels" . | nindent 4 }} +subjects: + - kind: ServiceAccount + name: dynatrace-operator + namespace: {{ .Release.Namespace }} +roleRef: + kind: ClusterRole + name: dynatrace-operator + apiGroup: rbac.authorization.k8s.io diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/operator/deployment-operator.yaml b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/operator/deployment-operator.yaml new file mode 100644 index 000000000..10143d58e --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/operator/deployment-operator.yaml @@ -0,0 +1,111 @@ +# Copyright 2021 Dynatrace LLC + +# 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. +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dynatrace-operator + namespace: {{ .Release.Namespace }} + annotations: + {{- if .Values.operator.annotations }} + {{- toYaml .Values.operator.annotations | nindent 4 }} + {{- end }} + labels: + dynatrace.com/install-source: {{ include "dynatrace-operator.installSource" . }} + {{- include "dynatrace-operator.operatorLabels" . | nindent 4 }} + {{- if .Values.operator.labels }} + {{- toYaml .Values.operator.labels | nindent 4 }} + {{- end }} +spec: + replicas: 1 + revisionHistoryLimit: 1 + selector: + matchLabels: + {{- include "dynatrace-operator.operatorSelectorLabels" . | nindent 6 }} + strategy: + type: RollingUpdate + template: + metadata: + annotations: + dynatrace.com/inject: "false" + {{- if (.Values.operator).apparmor}} + container.apparmor.security.beta.kubernetes.io/operator: runtime/default + {{- end }} + {{- if .Values.operator.annotations }} + {{- toYaml .Values.operator.annotations | nindent 8 }} + {{- end }} + labels: + {{- include "dynatrace-operator.operatorLabels" . | nindent 8 }} + {{- include "dynatrace-operator.operatorSelectorLabels" . | nindent 8 }} + {{- if .Values.operator.labels }} + {{- toYaml .Values.operator.labels | nindent 8 }} + {{- end }} + spec: + containers: + - name: operator + args: + - operator + # Replace this with the built image name + image: {{ include "dynatrace-operator.image" . }} + imagePullPolicy: Always + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + ports: + - containerPort: 10080 + name: livez + - containerPort: 8080 + name: metrics + resources: + requests: + {{- toYaml (.Values.operator).requests | nindent 14 }} + limits: + {{- toYaml (.Values.operator).limits | nindent 14 }} + volumeMounts: + - name: tmp-cert-dir + mountPath: /tmp/dynatrace-operator + livenessProbe: + httpGet: + path: /livez + port: livez + scheme: HTTP + initialDelaySeconds: 15 + periodSeconds: 10 + {{- include "dynatrace-operator.startupProbe" . | nindent 10 }} + securityContext: + {{- toYaml .Values.operator.securityContext | nindent 12 }} + {{- include "dynatrace-operator.nodeAffinity" . | nindent 6 }} + volumes: + - emptyDir: { } + name: tmp-cert-dir + serviceAccountName: dynatrace-operator + securityContext: + {{- toYaml .Values.operator.podSecurityContext | nindent 8 }} + {{- if .Values.customPullSecret }} + imagePullSecrets: + - name: {{ .Values.customPullSecret }} + {{- end }} + {{- if .Values.operator.nodeSelector }} + nodeSelector: {{- toYaml .Values.operator.nodeSelector | nindent 8 }} + {{- end }} + tolerations: + {{- if .Values.operator.tolerations }} + {{- toYaml .Values.operator.tolerations | nindent 8 }} + {{- end }} + {{- include "dynatrace-operator.defaultTolerations" . | nindent 8 }} diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/operator/role-operator.yaml b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/operator/role-operator.yaml new file mode 100644 index 000000000..d966ec8c0 --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/operator/role-operator.yaml @@ -0,0 +1,170 @@ +# Copyright 2021 Dynatrace LLC + +# 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. +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: dynatrace-operator + namespace: {{ .Release.Namespace }} + labels: + {{- include "dynatrace-operator.operatorLabels" . | nindent 4 }} +rules: + - apiGroups: + - dynatrace.com + resources: + - dynakubes + - edgeconnects + verbs: + - get + - list + - watch + - update + - create + - apiGroups: + - dynatrace.com + resources: + - dynakubes/finalizers + - dynakubes/status + - edgeconnects/finalizers + - edgeconnects/status + verbs: + - update + - apiGroups: + - apps + resources: + - statefulsets + verbs: + - get + - list + - watch + - create + - update + - delete + - apiGroups: + - apps + resources: + - daemonsets + verbs: + - get + - list + - watch + - create + - update + - delete + - apiGroups: + - apps + resources: + - replicasets + - deployments + verbs: + - get + - list + - watch + - create + - update + - delete + - apiGroups: + - apps + resources: + - deployments/finalizers + verbs: + - update + - apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - delete + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - secrets + verbs: + - get + - list + - watch + - create + - update + - delete + - apiGroups: + - "" + resources: + - events + verbs: + - create + - get + - list + - apiGroups: + - "" + resources: + - services + verbs: + - create + - update + - delete + - get + - list + - watch + - apiGroups: + - "" + resources: + - pods/log + verbs: + - get + - apiGroups: + - networking.istio.io + resources: + - serviceentries + - virtualservices + verbs: + - get + - list + - create + - update + - delete + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - update + - create +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: dynatrace-operator + namespace: {{ .Release.Namespace }} + labels: + {{- include "dynatrace-operator.operatorLabels" . | nindent 4 }} +subjects: + - kind: ServiceAccount + name: dynatrace-operator +roleRef: + kind: Role + name: dynatrace-operator + apiGroup: rbac.authorization.k8s.io diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/operator/serviceaccount-operator.yaml b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/operator/serviceaccount-operator.yaml new file mode 100644 index 000000000..0dfb23aed --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/operator/serviceaccount-operator.yaml @@ -0,0 +1,20 @@ +# Copyright 2021 Dynatrace LLC + +# 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. +apiVersion: v1 +kind: ServiceAccount +metadata: + name: dynatrace-operator + namespace: {{ .Release.Namespace }} + labels: + {{- include "dynatrace-operator.operatorLabels" . | nindent 4 }} diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/clusterrole-webhook.yaml b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/clusterrole-webhook.yaml new file mode 100644 index 000000000..953802aeb --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/clusterrole-webhook.yaml @@ -0,0 +1,102 @@ +# Copyright 2021 Dynatrace LLC + +# 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. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: dynatrace-webhook + labels: + {{- include "dynatrace-operator.webhookLabels" . | nindent 4 }} +rules: + - apiGroups: + - "" + resources: + - namespaces + verbs: + - get + - list + - watch + - update + - apiGroups: + - "" + resources: + - secrets + verbs: + - create + - apiGroups: + - "" + resources: + - secrets + resourceNames: + - dynatrace-dynakube-config + - dynatrace-metadata-enrichment-endpoint + verbs: + - get + - list + - watch + - update + # metadata-enrichment workload owner lookup + - apiGroups: + - "" + resources: + - replicationcontrollers + verbs: + - get + - apiGroups: + - apps + resources: + - replicasets + - statefulsets + - daemonsets + - deployments + verbs: + - get + - apiGroups: + - batch + resources: + - jobs + - cronjobs + verbs: + - get + - apiGroups: + - apps.openshift.io + resources: + - deploymentconfigs + verbs: + - get + {{- if (eq (include "dynatrace-operator.openshiftOrOlm" .) "true") }} + - apiGroups: + - security.openshift.io + resourceNames: + - privileged + - nonroot-v2 + resources: + - securitycontextconstraints + verbs: + - use + {{ end }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: dynatrace-webhook + labels: + {{- include "dynatrace-operator.webhookLabels" . | nindent 4 }} +subjects: + - kind: ServiceAccount + name: dynatrace-webhook + namespace: {{ .Release.Namespace }} +roleRef: + kind: ClusterRole + name: dynatrace-webhook + apiGroup: rbac.authorization.k8s.io diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/deployment-webhook.yaml b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/deployment-webhook.yaml new file mode 100644 index 000000000..f63530e86 --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/deployment-webhook.yaml @@ -0,0 +1,138 @@ +# Copyright 2021 Dynatrace LLC + +# 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. +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dynatrace-webhook + namespace: {{ .Release.Namespace }} + annotations: + {{- if .Values.webhook.annotations}} + {{- toYaml .Values.webhook.annotations | nindent 4 }} + {{- end }} + labels: + {{- include "dynatrace-operator.webhookLabels" . | nindent 4 }} + {{- if .Values.webhook.labels }} + {{- toYaml .Values.webhook.labels | nindent 4 }} + {{- end }} +spec: + replicas: {{ (default false (.Values.webhook).highAvailability) | ternary 2 1 }} + revisionHistoryLimit: 1 + selector: + matchLabels: + {{- include "dynatrace-operator.webhookSelectorLabels" . | nindent 6 }} + strategy: + type: RollingUpdate + template: + metadata: + annotations: + dynatrace.com/inject: "false" + kubectl.kubernetes.io/default-container: webhook + {{- if (.Values.webhook).apparmor}} + container.apparmor.security.beta.kubernetes.io/webhook: runtime/default + {{- end }} + {{- if .Values.webhook.annotations}} + {{- toYaml .Values.webhook.annotations | nindent 8 }} + {{- end }} + labels: + {{- include "dynatrace-operator.webhookLabels" . | nindent 8 }} + {{- include "dynatrace-operator.webhookSelectorLabels" . | nindent 8 }} + {{- if .Values.webhook.labels }} + {{- toYaml .Values.webhook.labels | nindent 8 }} + {{- end }} + spec: + {{- if (.Values.webhook).highAvailability }} + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: "topology.kubernetes.io/zone" + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + {{- include "dynatrace-operator.webhookSelectorLabels" . | nindent 14 }} + - maxSkew: 1 + topologyKey: "kubernetes.io/hostname" + whenUnsatisfiable: DoNotSchedule + labelSelector: + matchLabels: + {{- include "dynatrace-operator.webhookSelectorLabels" . | nindent 14 }} + {{- end }} + volumes: + - emptyDir: {} + name: certs-dir + {{- include "dynatrace-operator.nodeAffinity" . | nindent 6 }} + containers: + - name: webhook + args: + - webhook-server + # OLM mounts the certificates here, so we reuse it for simplicity + - --certs-dir=/tmp/k8s-webhook-server/serving-certs/ + image: {{ include "dynatrace-operator.image" . }} + imagePullPolicy: Always + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + readinessProbe: + httpGet: + path: /readyz + port: livez + scheme: HTTP + initialDelaySeconds: 15 + periodSeconds: 10 + livenessProbe: + httpGet: + path: /livez + port: livez + scheme: HTTP + initialDelaySeconds: 15 + periodSeconds: 10 + ports: + - name: server-port + containerPort: 8443 + - name: livez + containerPort: 10080 + - name: metrics + containerPort: 8080 + resources: + requests: + {{- toYaml (.Values.webhook).requests | nindent 14 }} + limits: + {{- toYaml (.Values.webhook).limits | nindent 14 }} + volumeMounts: + - name: certs-dir + mountPath: /tmp/k8s-webhook-server/serving-certs/ + securityContext: + {{- toYaml .Values.webhook.securityContext | nindent 12 }} + serviceAccountName: dynatrace-webhook + {{- if (.Values.webhook).hostNetwork }} + hostNetwork: true + {{- end }} + securityContext: + {{- toYaml .Values.webhook.podSecurityContext | nindent 8 }} + {{- if .Values.customPullSecret }} + imagePullSecrets: + - name: {{ .Values.customPullSecret }} + {{- end }} + {{- if .Values.webhook.nodeSelector }} + nodeSelector: {{- toYaml .Values.webhook.nodeSelector | nindent 8 }} + {{- end }} + tolerations: + {{- if .Values.webhook.tolerations }} + {{- toYaml .Values.webhook.tolerations | nindent 8 }} + {{- end }} + {{- include "dynatrace-operator.defaultTolerations" . | nindent 8 }} diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/mutatingwebhookconfiguration.yaml b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/mutatingwebhookconfiguration.yaml new file mode 100644 index 000000000..c7ec1baa5 --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/mutatingwebhookconfiguration.yaml @@ -0,0 +1,58 @@ +# Copyright 2021 Dynatrace LLC + +# 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. +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + name: dynatrace-webhook + labels: + {{- include "dynatrace-operator.webhookLabels" . | nindent 4 }} +webhooks: + - name: webhook.pod.dynatrace.com + reinvocationPolicy: IfNeeded + failurePolicy: {{.Values.webhook.mutatingWebhook.failurePolicy}} + timeoutSeconds: {{.Values.webhook.mutatingWebhook.timeoutSeconds}} + rules: + - apiGroups: [ "" ] + apiVersions: [ "v1" ] + operations: [ "CREATE" ] + resources: [ "pods" ] + scope: Namespaced + namespaceSelector: + matchExpressions: + - key: dynakube.internal.dynatrace.com/instance + operator: Exists + clientConfig: + service: + name: dynatrace-webhook + namespace: {{ .Release.Namespace }} + path: /inject + admissionReviewVersions: [ "v1beta1", "v1" ] + sideEffects: None + - name: webhook.ns.dynatrace.com + reinvocationPolicy: IfNeeded + failurePolicy: {{.Values.webhook.mutatingWebhook.failurePolicy}} + timeoutSeconds: {{.Values.webhook.mutatingWebhook.timeoutSeconds}} + rules: + - apiGroups: [ "" ] + apiVersions: [ "v1" ] + operations: [ "CREATE", "UPDATE"] + resources: [ "namespaces" ] + scope: Cluster + clientConfig: + service: + name: dynatrace-webhook + namespace: {{ .Release.Namespace }} + path: /label-ns + admissionReviewVersions: [ "v1beta1", "v1" ] + sideEffects: None diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/poddisruptionbudget-webhook.yaml b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/poddisruptionbudget-webhook.yaml new file mode 100644 index 000000000..cc188e449 --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/poddisruptionbudget-webhook.yaml @@ -0,0 +1,11 @@ +# v1 version supported since k8s 1.21 +apiVersion: {{ .Capabilities.APIVersions.Has "policy/v1" | ternary "policy/v1" "policy/v1beta1" }} +kind: PodDisruptionBudget +metadata: + name: dynatrace-webhook + namespace: {{ .Release.Namespace }} +spec: + minAvailable: 1 + selector: + matchLabels: + app.kubernetes.io/component: webhook diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/role-webhook.yaml b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/role-webhook.yaml new file mode 100644 index 000000000..1251ae47c --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/role-webhook.yaml @@ -0,0 +1,70 @@ +# Copyright 2021 Dynatrace LLC + +# 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. +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: dynatrace-webhook + namespace: {{ .Release.Namespace }} + labels: + {{- include "dynatrace-operator.webhookLabels" . | nindent 4 }} +rules: + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + - apiGroups: + - "" + resources: + - secrets + - pods + - configmaps + verbs: + - get + - list + - watch + - apiGroups: + - dynatrace.com + resources: + - dynakubes + verbs: + - get + - list + - watch + - apiGroups: + - apps + resources: + - daemonsets + verbs: + - get + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: dynatrace-webhook + namespace: {{ .Release.Namespace }} + labels: + {{- include "dynatrace-operator.webhookLabels" . | nindent 4 }} +subjects: + - kind: ServiceAccount + name: dynatrace-webhook + namespace: {{ .Release.Namespace }} +roleRef: + kind: Role + name: dynatrace-webhook + apiGroup: rbac.authorization.k8s.io diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/service.yaml b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/service.yaml new file mode 100644 index 000000000..5e0c94c1b --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/service.yaml @@ -0,0 +1,27 @@ +# Copyright 2021 Dynatrace LLC + +# 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. +apiVersion: v1 +kind: Service +metadata: + name: dynatrace-webhook + namespace: {{ .Release.Namespace }} + labels: + {{- include "dynatrace-operator.webhookLabels" . | nindent 4 }} +spec: + selector: + {{- include "dynatrace-operator.webhookLabels" . | nindent 4 }} + ports: + - port: 443 + protocol: TCP + targetPort: server-port diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/serviceaccount-webhook.yaml b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/serviceaccount-webhook.yaml new file mode 100644 index 000000000..d932bcca1 --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/serviceaccount-webhook.yaml @@ -0,0 +1,21 @@ +# Copyright 2021 Dynatrace LLC + +# 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. +apiVersion: v1 +kind: ServiceAccount +metadata: + name: dynatrace-webhook + namespace: {{ .Release.Namespace }} + labels: + {{- include "dynatrace-operator.webhookLabels" . | nindent 4 }} + diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/validatingwebhookconfiguration.yaml b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/validatingwebhookconfiguration.yaml new file mode 100644 index 000000000..f6c6cc868 --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/Common/webhook/validatingwebhookconfiguration.yaml @@ -0,0 +1,104 @@ +# Copyright 2021 Dynatrace LLC + +# 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. +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: dynatrace-webhook + labels: + {{- include "dynatrace-operator.webhookLabels" . | nindent 4 }} +webhooks: + - admissionReviewVersions: + - v1 + clientConfig: + service: + name: dynatrace-webhook + namespace: {{ .Release.Namespace }} + path: /validate-dynatrace-com-v1beta1-dynakube + rules: + - operations: + - CREATE + - UPDATE + apiGroups: + - dynatrace.com + apiVersions: + - v1beta1 + resources: + - dynakubes + name: v1beta1.dynakube.webhook.dynatrace.com + timeoutSeconds: {{.Values.webhook.validatingWebhook.timeoutSeconds}} + sideEffects: None + matchPolicy: Exact + - admissionReviewVersions: + - v1 + clientConfig: + service: + name: dynatrace-webhook + namespace: {{ .Release.Namespace }} + path: /validate-dynatrace-com-v1beta2-dynakube + rules: + - operations: + - CREATE + - UPDATE + apiGroups: + - dynatrace.com + apiVersions: + - v1beta2 + resources: + - dynakubes + name: v1beta2.dynakube.webhook.dynatrace.com + timeoutSeconds: {{.Values.webhook.validatingWebhook.timeoutSeconds}} + sideEffects: None + matchPolicy: Exact + - admissionReviewVersions: + - v1 + clientConfig: + service: + name: dynatrace-webhook + namespace: {{ .Release.Namespace }} + path: /validate-dynatrace-com-v1alpha1-edgeconnect + rules: + - operations: + - CREATE + - UPDATE + apiGroups: + - dynatrace.com + apiVersions: + - v1alpha1 + resources: + - edgeconnects + name: v1alpha1.edgeconnect.webhook.dynatrace.com + timeoutSeconds: {{.Values.webhook.validatingWebhook.timeoutSeconds}} + sideEffects: None + matchPolicy: Exact + - admissionReviewVersions: + - v1 + clientConfig: + service: + name: dynatrace-webhook + namespace: {{ .Release.Namespace }} + path: /validate-dynatrace-com-v1alpha2-edgeconnect + rules: + - operations: + - CREATE + - UPDATE + apiGroups: + - dynatrace.com + apiVersions: + - v1alpha2 + resources: + - edgeconnects + name: v1alpha2.edgeconnect.webhook.dynatrace.com + timeoutSeconds: {{.Values.webhook.validatingWebhook.timeoutSeconds}} + sideEffects: None + matchPolicy: Exact diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/NOTES.txt b/charts/dynatrace/dynatrace-operator/1.3.2/templates/NOTES.txt new file mode 100644 index 000000000..48d4f09fc --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/NOTES.txt @@ -0,0 +1,10 @@ +Thank you for installing {{ .Chart.Name }}. + +Your release is named {{ .Release.Name }}. + +To find more information about the Dynatrace Operator, try: +https://github.com/Dynatrace/dynatrace-operator + +To verify the current state of the deployments, try: + $ kubectl get pods -n {{ .Release.Namespace }} + $ kubectl logs -f deployment/dynatrace-operator -n {{ .Release.Namespace }} diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/_csidriver.tpl b/charts/dynatrace/dynatrace-operator/1.3.2/templates/_csidriver.tpl new file mode 100644 index 000000000..08ee79fb5 --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/_csidriver.tpl @@ -0,0 +1,74 @@ +// Copyright 2020 Dynatrace LLC + +// 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. + +{{/* +Check if we need the csi driver. +*/}} +{{- define "dynatrace-operator.needCSI" -}} + {{- if or (.Values.csidriver.enabled) -}} + {{- printf "true" -}} + {{- end -}} +{{- end -}} + +{{/* +CSI PriorityClassName +*/}} +{{- define "dynatrace-operator.CSIPriorityClassName" -}} + {{- default "dynatrace-high-priority" .Values.csidriver.existingPriorityClassName -}} +{{- end -}} + +{{/* +Check if we need the csi default priority class +*/}} +{{- define "dynatrace-operator.needPriorityClass" -}} + {{- if and (eq (include "dynatrace-operator.needCSI" .) "true") (not .Values.csidriver.existingPriorityClassName) -}} + {{- printf "true" -}} + {{- end -}} +{{- end -}} + +{{/* +CSI plugin-dir path +*/}} +{{- define "dynatrace-operator.CSIPluginDir" -}} + {{ printf "%s/plugins/csi.oneagent.dynatrace.com/" (trimSuffix "/" (default "/var/lib/kubelet" .Values.csidriver.kubeletPath)) }} +{{- end -}} + + +{{/* +CSI data-dir path +*/}} +{{- define "dynatrace-operator.CSIDataDir" -}} + {{ printf "%s/data" (trimSuffix "/" (include "dynatrace-operator.CSIPluginDir" .)) }} +{{- end -}} + +{{/* +CSI socket path +*/}} +{{- define "dynatrace-operator.CSISocketPath" -}} + {{ printf "%s/csi.sock" (trimSuffix "/" (include "dynatrace-operator.CSIPluginDir" .)) }} +{{- end -}} + +{{/* +CSI mountpoint-dir path +*/}} +{{- define "dynatrace-operator.CSIMountPointDir" -}} + {{ printf "%s/pods/" (trimSuffix "/" (default "/var/lib/kubelet" .Values.csidriver.kubeletPath)) }} +{{- end -}} + +{{/* +CSI registration-dir path +*/}} +{{- define "dynatrace-operator.CSIRegistrationDir" -}} + {{ printf "%s/plugins_registry/" (trimSuffix "/" (default "/var/lib/kubelet" .Values.csidriver.kubeletPath)) }} +{{- end -}} diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/_helpers.tpl b/charts/dynatrace/dynatrace-operator/1.3.2/templates/_helpers.tpl new file mode 100644 index 000000000..1f17f8068 --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/_helpers.tpl @@ -0,0 +1,53 @@ +// Copyright 2020 Dynatrace LLC + +// 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. + + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "dynatrace-operator.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Check if default image or imageref is used +*/}} +{{- define "dynatrace-operator.image" -}} +{{- if .Values.image -}} + {{- printf "%s" .Values.image -}} +{{- else -}} + {{- if (.Values.imageRef).repository -}} + {{- .Values.imageRef.tag | default (printf "v%s" .Chart.AppVersion) | printf "%s:%s" .Values.imageRef.repository -}} + {{- else if eq (include "dynatrace-operator.platform" .) "openshift" -}} + {{- printf "%s:v%s" "registry.connect.redhat.com/dynatrace/dynatrace-operator" .Chart.AppVersion }} + {{- else if eq (include "dynatrace-operator.platform" .) "google-marketplace" -}} + {{- printf "%s:%s" "gcr.io/dynatrace-marketplace-prod/dynatrace-operator" .Chart.AppVersion }} + {{- else if eq (include "dynatrace-operator.platform" .) "azure-marketplace" -}} + {{- printf "%s/%s@%s" .Values.global.azure.images.operator.registry .Values.global.azure.images.operator.image .Values.global.azure.images.operator.digest }} + {{- else -}} + {{- printf "%s:v%s" "public.ecr.aws/dynatrace/dynatrace-operator" .Chart.AppVersion }} + {{- end -}} +{{- end -}} +{{- end -}} + +{{- define "dynatrace-operator.startupProbe" -}} +startupProbe: + exec: + command: + - /usr/local/bin/dynatrace-operator + - startup-probe + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 1 +{{- end -}} diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/_labels.tpl b/charts/dynatrace/dynatrace-operator/1.3.2/templates/_labels.tpl new file mode 100644 index 000000000..e66473db0 --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/_labels.tpl @@ -0,0 +1,102 @@ +// Copyright 2020 Dynatrace LLC + +// 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. + +{{/* +Selector labels +*/}} +{{- define "dynatrace-operator.futureSelectorLabels" -}} +app.kubernetes.io/name: dynatrace-operator +{{- if not (.Values).manifests }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} +{{- end -}} + +{{/* +Common labels +*/}} +{{- define "dynatrace-operator.commonLabels" -}} +{{ include "dynatrace-operator.futureSelectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +{{- if not (.Values).manifests }} +helm.sh/chart: {{ include "dynatrace-operator.chart" . }} +{{- end -}} +{{- if eq (include "dynatrace-operator.platform" .) "azure-marketplace" }} +azure-extensions-usage-release-identifier: {{ .Release.Name | quote }} +{{- end -}} +{{- end -}} + +{{/* +Operator labels +*/}} +{{- define "dynatrace-operator.operatorLabels" -}} +{{ include "dynatrace-operator.commonLabels" . }} +app.kubernetes.io/component: operator +{{- end -}} + +{{/* +Operator selector labels +*/}} +{{- define "dynatrace-operator.operatorSelectorLabels" -}} +name: {{ .Release.Name }} +{{- end -}} + +{{/* +Webhook labels +*/}} +{{- define "dynatrace-operator.webhookLabels" -}} +{{ include "dynatrace-operator.commonLabels" . }} +app.kubernetes.io/component: webhook +{{- end -}} + +{{/* +Webhook selector labels +*/}} +{{- define "dynatrace-operator.webhookSelectorLabels" -}} +internal.dynatrace.com/component: webhook +internal.dynatrace.com/app: webhook +{{- end -}} + +{{/* +CSI labels +*/}} +{{- define "dynatrace-operator.csiLabels" -}} +{{ include "dynatrace-operator.commonLabels" . }} +app.kubernetes.io/component: csi-driver +{{- end -}} + +{{/* +CSI selector labels +*/}} +{{- define "dynatrace-operator.csiSelectorLabels" -}} +internal.oneagent.dynatrace.com/app: csi-driver +internal.oneagent.dynatrace.com/component: csi-driver +{{- end -}} + +{{/* +ActiveGate labels +*/}} +{{- define "dynatrace-operator.activegateLabels" -}} +{{ include "dynatrace-operator.commonLabels" . }} +app.kubernetes.io/component: activegate +{{- end -}} + +{{/* +OneAgent labels +*/}} +{{- define "dynatrace-operator.oneagentLabels" -}} +{{ include "dynatrace-operator.commonLabels" . }} +app.kubernetes.io/component: oneagent +{{- end -}} diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/_platform.tpl b/charts/dynatrace/dynatrace-operator/1.3.2/templates/_platform.tpl new file mode 100644 index 000000000..2080d801e --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/_platform.tpl @@ -0,0 +1,84 @@ +// Copyright 2020 Dynatrace LLC + +// 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. + +{{/* +Auto-detect the platform (if not set), according to the available APIVersions +*/}} +{{- define "dynatrace-operator.platform" -}} + {{- if .Values.platform}} + {{- printf .Values.platform -}} + {{- else if .Capabilities.APIVersions.Has "security.openshift.io/v1" }} + {{- printf "openshift" -}} + {{- else }} + {{- printf "kubernetes" -}} + {{- end -}} +{{- end }} + +{{/* +Set install source how the Operator was installed +*/}} +{{- define "dynatrace-operator.installSource" -}} + {{- if .Values.olm }} + {{- printf "operatorhub" -}} + {{- else if .Values.manifests }} + {{- printf "manifest" -}} + {{- else if (and (.Values.platform) (not (has .Values.platform (list "kubernetes" "openshift")))) }} + {{- printf .Values.platform -}} + {{- else }} + {{- printf "helm" -}} + {{- end -}} +{{- end }} + +{{/* +Exclude Kubernetes manifest not running on OLM +*/}} +{{- define "dynatrace-operator.openshiftOrOlm" -}} +{{- if and (or (eq (include "dynatrace-operator.platform" .) "openshift") (.Values.olm)) -}} + {{ default "true" }} +{{- end -}} +{{- end -}} + +{{- define "dynatrace-operator.nodeAffinity" -}} +affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/arch + operator: In + values: + - amd64 + - arm64 + - ppc64le + - s390x + - key: kubernetes.io/os + operator: In + values: + - linux +{{- end -}} + +{{- define "dynatrace-operator.defaultTolerations" -}} +- key: kubernetes.io/arch + value: arm64 + effect: NoSchedule +- key: kubernetes.io/arch + value: amd64 + effect: NoSchedule +- key: kubernetes.io/arch + value: ppc64le + effect: NoSchedule +- key: kubernetes.io/arch + value: s390x + effect: NoSchedule +{{- end -}} diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/templates/application.yaml b/charts/dynatrace/dynatrace-operator/1.3.2/templates/application.yaml new file mode 100644 index 000000000..1dd17410d --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/templates/application.yaml @@ -0,0 +1,98 @@ +{{- if eq (include "dynatrace-operator.platform" .) "google-marketplace" }} +# Copyright 2020 Dynatrace LLC + +# 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. +apiVersion: app.k8s.io/v1beta1 +kind: Application +metadata: + name: {{ .Release.Name }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "dynatrace-operator.operatorLabels" . | nindent 4 }} + annotations: + kubernetes-engine.cloud.google.com/icon: data:image/png;base64,{{ .Files.Get "logo.png" | b64enc }} + marketplace.cloud.google.com/deploy-info: '{"partner_id": "dynatrace-marketplace-prod", "product_id": "dynatrace-operator", "partner_name": "Dynatrace LLC"}' +spec: + descriptor: + type: "Dynatrace Operator" + version: {{ .Chart.AppVersion }} + maintainers: + - name: Dynatrace LLC + url: https://www.dynatrace.com/ + keywords: + - "dynatrace" + - "operator" + - "activegate" + - "k8s" + - "monitoring" + - "apm" + description: | + # Dynatrace Operator + + The Dynatrace Operator supports rollout and lifecycle management of various Dynatrace components in Kubernetes and OpenShift. + + * OneAgent + * `classicFullStack` rolls out a OneAgent pod per node to monitor pods on it and the node itself + * `applicationMonitoring` is a webhook based injection mechanism for automatic app-only injection + * CSI Driver can be enabled to cache OneAgent downloads per node + * `hostMonitoring` is only monitoring the hosts (i.e. nodes) in the cluster without app-only injection + * `cloudNativeFullStack` is a combination of `applicationMonitoring` with CSI driver and `hostMonitoring` + * ActiveGate + * `routing` routes OneAgent traffic through the ActiveGate + * `kubernetes-monitoring` allows monitoring of the Kubernetes API + * `metrics-ingest` routes enriched metrics through ActiveGate + + For more information please have a look at [our DynaKube Custom Resource examples](config/samples) and + our [official help page](https://www.dynatrace.com/support/help/setup-and-configuration/setup-on-container-platforms/kubernetes/). + links: + - description: Dynatrace Website + url: https://www.dynatrace.com/ + - description: Operator Deploy Guide + url: ToDo + - description: Kubernetes Monitoring Info + url: https://www.dynatrace.com/technologies/kubernetes-monitoring + selector: + matchLabels: + app.kubernetes.io/name: dynatrace-operator + componentKinds: + - group: apps/v1 + kind: DaemonSet + - group: v1 + kind: Pod + - group: v1 + kind: ConfigMap + - group: apps/v1 + kind: Deployment + - group: v1 + kind: Secret + - group: batch/v1 + kind: Job + - group: v1 + kind: Service + - group: v1 + kind: ServiceAccount + - group: admissionregistration.k8s.io/v1 + kind: ValidatingWebhookConfiguration + - group: admissionregistration.k8s.io/v1 + kind: MutatingWebhookConfiguration + - group: apps/v1 + kind: StatefulSet + - group: storage.k8s.io/v1 + kind: CSIDriver + - group: rbac.authorization.k8s.io/v1 + kind: ClusterRole + - group: rbac.authorization.k8s.io/v1 + kind: ClusterRoleBinding + - group: rbac.authorization.k8s.io/v1 + kind: Role + - group: rbac.authorization.k8s.io/v1 + kind: RoleBinding +{{ end }} diff --git a/charts/dynatrace/dynatrace-operator/1.3.2/values.yaml b/charts/dynatrace/dynatrace-operator/1.3.2/values.yaml new file mode 100644 index 000000000..0c484e7ba --- /dev/null +++ b/charts/dynatrace/dynatrace-operator/1.3.2/values.yaml @@ -0,0 +1,196 @@ +# Copyright 2021 Dynatrace LLC + +# 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. + +# special handling for "openshift" and "gke-autopilot" (deprecated) +platform: "" + +#image qualifier; OBSOLETE -> use imageref instead! +# supply either image or imageref; if both supplied, imageref will be disregarded +image: "" +#image description using tags +#resulting image will be named :v +imageRef: + repository: "" #path to repo + tag: "" #defaults to chart version + +customPullSecret: "" +installCRD: true + +operator: + nodeSelector: {} + tolerations: [] + labels: {} + annotations: {} + apparmor: false + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1001 + runAsGroup: 1001 + capabilities: + drop: + - ALL + seccompProfile: + type: RuntimeDefault + podSecurityContext: + seccompProfile: + type: RuntimeDefault + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: 100m + memory: 128Mi + +webhook: + hostNetwork: false + nodeSelector: {} + tolerations: [] + labels: {} + annotations: {} + apparmor: false + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1001 + runAsGroup: 1001 + capabilities: + drop: + - ALL + seccompProfile: + type: RuntimeDefault + podSecurityContext: + seccompProfile: + type: RuntimeDefault + requests: + cpu: 300m + memory: 128Mi + limits: + cpu: 300m + memory: 128Mi + highAvailability: true + validatingWebhook: + timeoutSeconds: 10 + mutatingWebhook: + failurePolicy: Ignore + timeoutSeconds: 10 + +csidriver: + enabled: true + nodeSelector: {} + kubeletPath: "/var/lib/kubelet" + existingPriorityClassName: "" # if defined, use this priorityclass instead of creating a new one + priorityClassValue: "1000000" + maxUnmountedVolumeAge: "" # defined in days, must be a plain number + tolerations: + - effect: NoSchedule + key: node-role.kubernetes.io/master + operator: Exists + - effect: NoSchedule + key: node-role.kubernetes.io/control-plane + operator: Exists + labels: {} + annotations: {} + updateStrategy: + rollingUpdate: + maxSurge: 0 + maxUnavailable: 1 + type: RollingUpdate + csiInit: + securityContext: + runAsUser: 0 + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: false + seLinuxOptions: + level: s0 + seccompProfile: + type: RuntimeDefault + resources: + requests: + cpu: 50m + memory: 100Mi + limits: + cpu: 50m + memory: 100Mi + server: + securityContext: + runAsUser: 0 + privileged: true # Needed for mountPropagation + allowPrivilegeEscalation: true # Needed for privileged + readOnlyRootFilesystem: true + runAsNonRoot: false + seLinuxOptions: + level: s0 + seccompProfile: + type: RuntimeDefault + resources: + requests: + cpu: 50m + memory: 100Mi + limits: + cpu: 50m + memory: 100Mi + provisioner: + securityContext: + runAsUser: 0 + privileged: true # Needed for mountPropagation + allowPrivilegeEscalation: true # Needed for privileged + readOnlyRootFilesystem: true + runAsNonRoot: false + seLinuxOptions: + level: s0 + seccompProfile: + type: RuntimeDefault + resources: + requests: + cpu: 300m + memory: 100Mi + registrar: + securityContext: + runAsUser: 0 + privileged: false + readOnlyRootFilesystem: true + runAsNonRoot: false + seccompProfile: + type: RuntimeDefault + resources: + requests: + cpu: 20m + memory: 30Mi + limits: + cpu: 20m + memory: 30Mi + livenessprobe: + securityContext: + runAsUser: 0 + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: false + seccompProfile: + type: RuntimeDefault + resources: + requests: + cpu: 20m + memory: 30Mi + limits: + cpu: 20m + memory: 30Mi + diff --git a/charts/f5/f5-bigip-ctlr/0.0.3301/.helmignore b/charts/f5/f5-bigip-ctlr/0.0.3301/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/charts/f5/f5-bigip-ctlr/0.0.3301/.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/f5/f5-bigip-ctlr/0.0.3301/Chart.yaml b/charts/f5/f5-bigip-ctlr/0.0.3301/Chart.yaml new file mode 100644 index 000000000..e80019c72 --- /dev/null +++ b/charts/f5/f5-bigip-ctlr/0.0.3301/Chart.yaml @@ -0,0 +1,26 @@ +annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: F5 Container Ingress Services for Kubernetes and + OpenShift + catalog.cattle.io/kube-version: '>=1.20-0' + catalog.cattle.io/release-name: f5-bigip-ctlr +apiVersion: v1 +description: Deploy the F5 Networks BIG-IP Controller for Kubernetes and OpenShift + (k8s-bigip-ctlr). +home: https://www.f5.com/products/automation-and-orchestration/container-ingress-services +icon: file://assets/icons/f5-bigip-ctlr.png +keywords: +- F5 +- BIG-IP +- Containers +- Kubernetes +- OpenShift +kubeVersion: '>=1.20-0' +maintainers: +- email: f5_cis_operators@f5.com + name: F5CISSupport +name: f5-bigip-ctlr +sources: +- https://github.com/F5Networks/k8s-bigip-ctlr +- https://github.com/F5Networks/charts +version: 0.0.3301 diff --git a/charts/f5/f5-bigip-ctlr/0.0.3301/README.md b/charts/f5/f5-bigip-ctlr/0.0.3301/README.md new file mode 100644 index 000000000..0dc941a99 --- /dev/null +++ b/charts/f5/f5-bigip-ctlr/0.0.3301/README.md @@ -0,0 +1,93 @@ +# Helm Chart for the F5 Container Ingress Services + +This chart simplifies repeatable, versioned deployment of the [Container Ingress Services](https://clouddocs.f5.com/containers/latest/). + +### Prerequisites +- Refer to [CIS Prerequisites](https://clouddocs.f5.com/containers/latest/userguide/cis-helm.html#prerequisites) to install Container Ingress Services on Kubernetes or Openshift +- [Helm 3](https://helm.sh/docs/intro/) should be installed. + + +## Installing CIS Using Helm Charts + +This is the simplest way to install the CIS on OpenShift/Kubernetes cluster. Helm is a package manager for Kubernetes. Helm is Kubernetes version of yum or apt. Helm deploys something called charts, which you can think of as a packaged application. It is a collection of all your versioned, pre-configured application resources which can be deployed as one unit. This chart creates a Deployment for one Pod containing the [k8s-bigip-ctlr](https://clouddocs.f5.com/containers/latest/), it's supporting RBAC, Service Account and Custom Resources Definition installations. + +## Installing the Chart + +- (Optional) Add BIG-IP credentials as K8S secrets. + +For Kubernetes, use the following command: + +```kubectl create secret generic f5-bigip-ctlr-login -n kube-system --from-literal=username=admin --from-literal=password=``` + +For OpenShift, use the following command: + +```oc create secret generic f5-bigip-ctlr-login -n kube-system --from-literal=username=admin --from-literal=password=``` + +- Add the CIS chart repository in Helm using following command: + +```helm repo add f5-stable https://f5networks.github.io/charts/stable``` + +- Create values.yaml as shown in [examples](https://github.com/F5Networks/charts/tree/master/example_values/f5-bigip-ctlr): + +- Install the Helm chart if BIGIP credential secrets created manually using the following command: + +```helm install -f values.yaml f5-stable/f5-bigip-ctlr``` + +- Install the Helm chart with skip crds if BIGIP credential secrets created manually (without custom resource definitions installations) + +```helm install --skip-crds -f values.yaml f5-stable/f5-bigip-ctlr``` + +- If you want to create the BIGIP credential secret with helm charts use the following command: + +```helm install --set bigip_secret.create="true" --set bigip_secret.username=$BIGIP_USERNAME --set bigip_secret.password=$BIGIP_PASSWORD -f values.yaml f5-stable/f5-bigip-ctlr``` + +## Chart parameters: + +Parameter | Required | Description | Default +----------|-------------|-------------|-------- +bigip_login_secret | Optional | Secret that contains BIG-IP login credentials | f5-bigip-ctlr-login +args.bigip_url | Required | The management IP for your BIG-IP device | **Required**, no default +args.bigip_partition | Required | BIG-IP partition the CIS Controller will manage | f5-bigip-ctlr +args.namespaces | Optional | List of Kubernetes namespaces which CIS will monitor | empty +bigip_secret.create | Optional | Create kubernetes secret using username and password | false +bigip_secret.username | Optional | bigip username to create the kubernetes secret | empty +bigip_secret.password | Optional | bigip password to create the kubernetes secret | empty +rbac.create | Optional | Create ClusterRole and ClusterRoleBinding | true +serviceAccount.name | Optional | name of the ServiceAccount for CIS controller | f5-bigip-ctlr-serviceaccount +serviceAccount.create | Optional | Create service account for the CIS controller | true +namespace | Optional | name of namespace CIS will use to create deployment and other resources | kube-system +image.user | Optional | CIS Controller image repository username | f5networks +image.repo | Optional | CIS Controller image repository name | k8s-bigip-ctlr +image.pullPolicy | Optional | CIS Controller image pull policy | Always +image.pullSecrets | Optional | List of secrets of container registry to pull image | empty +version | Optional | CIS Controller image tag | latest +nodeSelector | Optional | dictionary of Node selector labels | empty +tolerations | Optional | Array of labels | empty +limits_cpu | Optional | CPU limits for the pod | 100m +limits_memory | Optional | Memory limits for the pod | 512Mi +requests_cpu | Optional | CPU request for the pod | 100m +requests_memory | Optional | Memory request for the pod | 512Mi +affinity | Optional | Dictionary of affinity | empty +securityContext | Optional | Dictionary of deployment securityContext | empty +podSecurityContext | Optional | Dictionary of pod securityContext | empty +ingressClass.ingressClassName | Optional | Name of ingress class | f5 +ingressClass.isDefaultIngressController | Optional | CIS will monitor all the ingresses resource if set true | false +ingressClass.create | Optional | Create ingress class | true + +Note: bigip_login_secret and bigip_secret are mutually exclusive, if both are defined in values.yaml file bigip_secret will be given priority. + + +See the CIS documentation for a full list of args supported for CIS [CIS Configuration Options](https://clouddocs.f5.com/containers/latest/userguide/config-parameters.html) + +> **Note:** Helm value names cannot include the character `-` which is commonly used in the names of parameters passed to the controller. To accomodate Helm, the parameter names in `values.yaml` use `_` and then replace them with `-` when rendering. +> e.g. `args.bigip_url` is rendered as `bigip-url` as required by the CIS Controller. + + +If you have a specific use case for F5 products in the Kubernetes environment that would benefit from a curated chart, please [open an issue](https://github.com/F5Networks/charts/issues) describing your use case and providing example resources. + +## Uninstalling Helm Chart + +Run the following command to uninstall the chart. + +```helm uninstall ``` + diff --git a/charts/f5/f5-bigip-ctlr/0.0.3301/app-readme.md b/charts/f5/f5-bigip-ctlr/0.0.3301/app-readme.md new file mode 100644 index 000000000..6f28ef779 --- /dev/null +++ b/charts/f5/f5-bigip-ctlr/0.0.3301/app-readme.md @@ -0,0 +1,87 @@ +# Helm Chart for the F5 Container Ingress Services + +This chart simplifies repeatable, versioned deployment of the [Container Ingress Services](https://clouddocs.f5.com/containers/latest/). + +### Prerequisites +- Refer to [CIS Prerequisites](https://clouddocs.f5.com/containers/latest/userguide/cis-helm.html#prerequisites) to install Container Ingress Services on Kubernetes or Openshift +- [Helm 3](https://helm.sh/docs/intro/) should be installed. + + +## Installing CIS Using Helm Charts + +This is the simplest way to install the CIS on OpenShift/Kubernetes cluster. Helm is a package manager for Kubernetes. Helm is Kubernetes version of yum or apt. Helm deploys something called charts, which you can think of as a packaged application. It is a collection of all your versioned, pre-configured application resources which can be deployed as one unit. This chart creates a Deployment for one Pod containing the [k8s-bigip-ctlr](https://clouddocs.f5.com/containers/latest/), it's supporting RBAC, Service Account and Custom Resources Definition installations. + +## Installing the Chart + +- Add BIG-IP credentials as K8S secrets. + +For Kubernetes, use the following command: + +```kubectl create secret generic f5-bigip-ctlr-login -n kube-system --from-literal=username=admin --from-literal=password=``` + +For OpenShift, use the following command: + +```oc create secret generic f5-bigip-ctlr-login -n kube-system --from-literal=username=admin --from-literal=password=``` + +- Add the CIS chart repository in Helm using following command: + +```helm repo add f5-stable https://f5networks.github.io/charts/stable``` + +- Create values.yaml as shown in [examples](https://github.com/F5Networks/charts/tree/master/example_values/f5-bigip-ctlr): + +- Install the Helm chart using the following command: + +```helm install -f values.yaml f5-stable/f5-bigip-ctlr``` + +- Install the Helm chart with skip crds (without custom resource definitions installations) + +```helm install --skip-crds -f values.yaml f5-stable/f5-bigip-ctlr``` + +## Chart parameters: + +Parameter | Required | Description | Default +----------|-------------|-------------|-------- +bigip_login_secret | Required | Secret that contains BIG-IP login credentials | f5-bigip-ctlr-login +args.bigip_url | Required | The management IP for your BIG-IP device | **Required**, no default +args.bigip_partition | Required | BIG-IP partition the CIS Controller will manage | f5-bigip-ctlr +args.namespaces | Optional | List of Kubernetes namespaces which CIS will monitor | empty +rbac.create | Optional | Create ClusterRole and ClusterRoleBinding | true +serviceAccount.name | Optional | name of the ServiceAccount for CIS controller | f5-bigip-ctlr-serviceaccount +serviceAccount.create | Optional | Create service account for the CIS controller | true +namespace | Optional | name of namespace CIS will use to create deployment and other resources | kube-system +image.user | Optional | CIS Controller image repository username | f5networks +image.repo | Optional | CIS Controller image repository name | k8s-bigip-ctlr +image.pullPolicy | Optional | CIS Controller image pull policy | Always +image.pullSecrets | Optional | List of secrets of container registry to pull image | empty +version | Optional | CIS Controller image tag | latest +nodeSelector | Optional | dictionary of Node selector labels | empty +tolerations | Optional | Array of labels | empty +limits_cpu | Optional | CPU limits for the pod | 100m +limits_memory | Optional | Memory limits for the pod | 512Mi +requests_cpu | Optional | CPU request for the pod | 100m +requests_memory | Optional | Memory request for the pod | 512Mi +affinity | Optional | Dictionary of affinity | empty +securityContext | Optional | Dictionary of securityContext | empty +ingressClass.ingressClassName | Optional | Name of ingress class | f5 +ingressClass.defaultIngressController | Optional | CIS will monitor all the ingresses resource if set true | false +ingressClass.create | Optional | Create ingress class | true + + + + + + +See the CIS documentation for a full list of args supported for CIS [CIS Configuration Options](https://clouddocs.f5.com/containers/latest/userguide/config-parameters.html) + +> **Note:** Helm value names cannot include the character `-` which is commonly used in the names of parameters passed to the controller. To accomodate Helm, the parameter names in `values.yaml` use `_` and then replace them with `-` when rendering. +> e.g. `args.bigip_url` is rendered as `bigip-url` as required by the CIS Controller. + + +If you have a specific use case for F5 products in the Kubernetes environment that would benefit from a curated chart, please [open an issue](https://github.com/F5Networks/charts/issues) describing your use case and providing example resources. + +## Uninstalling Helm Chart + +Run the following command to uninstall the chart. + +```helm uninstall ``` + diff --git a/charts/f5/f5-bigip-ctlr/0.0.3301/crds/f5-bigip-ctlr-customresourcedefinitions.yml b/charts/f5/f5-bigip-ctlr/0.0.3301/crds/f5-bigip-ctlr-customresourcedefinitions.yml new file mode 100644 index 000000000..5186ea541 --- /dev/null +++ b/charts/f5/f5-bigip-ctlr/0.0.3301/crds/f5-bigip-ctlr-customresourcedefinitions.yml @@ -0,0 +1,1342 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + labels: + app.kubernetes.io/instance: f5-bigip-ctlr + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: f5-bigip-ctlr + name: virtualservers.cis.f5.com +spec: + group: cis.f5.com + names: + kind: VirtualServer + plural: virtualservers + shortNames: + - vs + singular: virtualserver + scope: Namespaced + versions: + - + name: v1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + partition: + type: string + pattern: '^[a-zA-Z]+[-A-z0-9_.]+$' + host: + type: string + pattern: '^(([a-zA-Z0-9\*]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$' + hostAliases: + type: array + items: + type: string + pattern: '^(([a-zA-Z0-9\*]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$' + hostGroup: + type: string + pattern: '^[a-zA-Z]+[-A-z0-9_.:]*[A-z0-9]*$' + hostGroupVirtualServerName: + type: string + pattern: '^[a-zA-Z]+([A-z0-9-_+])*([A-z0-9])$' + httpTraffic: + type: string + enum: [allow, none, redirect] + ipamLabel: + type: string + pattern: '^[a-zA-Z]+[-A-z0-9_.:]+[A-z0-9]+$' + bigipRouteDomain: + type: integer + minimum: 0 + maximum: 65535 + default: 0 + snat: + type: string + pattern: '^$|^\/?[a-zA-Z]+([-A-z0-9_+]+\/)*([-A-z0-9_.:]+\/?)+$' + connectionMirroring: + type: string + enum: [ none, L4 ] + tlsProfileName: + type: string + pattern: '^[a-zA-Z]+[-A-z0-9_.:]+[A-z0-9]+$' + persistenceProfile: + type: string + pattern: '^\/?[a-zA-Z]+([-A-z0-9_+]+\/)*([-A-z0-9_.:]+\/?)*$' + hostPersistence: + type: object + properties: + method: + type: string + enum: [ sourceAddress, destinationAddress, cookieInsert, cookieRewrite, cookiePassive, cookieHash, universal, hash, carp, none ] + metaData: + type: object + properties: + name: + type: string + key: + type: string + netmask: + type: string + pattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])|(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$' + timeout: + type: integer + minimum: 1 + maximum: 65535 + offset: + type: integer + minimum: 1 + maximum: 65535 + length: + type: integer + minimum: 1 + maximum: 65535 + expiry: + type: string + pattern: '^((?:(?:[0-9]+d))|(?:(?:[0-9]+d)?((?:[01]?[0-9]|2[0-3]):[0-5][0-9](?::[0-5][0-9])?)))$' + required: + - method + htmlProfile: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + profiles: + type: object + properties: + tcp: + type: object + properties: + client: + type: string + pattern: '^\/([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + server: + type: string + pattern: '^\/([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + http2: + type: object + properties: + client: + type: string + pattern: '^\/([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + server: + type: string + pattern: '^\/([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + dos: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + profileAccess: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + policyPerRequestAccess: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + botDefense: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + policyName: + type: string + pattern: '^[a-zA-Z]+[-A-z0-9_.:]+[A-z0-9]+$' + rewriteAppRoot: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)*([-A-z0-9_.:]+\/?)*$' + waf: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + profileMultiplex: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + profileAdapt: + type: object + properties: + request: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + response: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + allowVlans: + items: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.]+\/?)*$' + type: array + allowSourceRange: + items: + type: string + type: array + httpMrfRoutingEnabled: + type: boolean + iRules: + type: array + items: + type: string + pattern: '^none$|^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + serviceAddress: + type: array + maxItems: 1 + items: + type: object + properties: + arpEnabled: + type: boolean + icmpEcho: + type: string + enum: [enable, disable, selective] + routeAdvertisement: + type: string + enum: [enable, disable, selective, always, any, all] + spanningEnabled: + type: boolean + trafficGroup: + type: string + pattern: '^\/([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + defaultPool: + type: object + properties: + name: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + service: + type: string + pattern: '[a-z]([-a-z0-9]*[a-z0-9])?' + servicePort: + x-kubernetes-int-or-string: true + anyOf: + - type: integer + - type: string + serviceNamespace: + type: string + pattern: '^[a-zA-Z]+([-A-z0-9_.+:])*([A-z0-9])+$' + loadBalancingMethod: + type: string + pattern: '^[a-z]+[a-z_-]+[a-z]+$' + nodeMemberLabel: + type: string + pattern: '^[a-zA-Z0-9][-A-Za-z0-9_.\/]{0,61}[a-zA-Z0-9]=[a-zA-Z0-9][-A-Za-z0-9_.]{0,61}[a-zA-Z0-9]$' + monitors: + type: array + items: + type: object + properties: + type: + type: string + enum: [ tcp, udp, http, https ] + interval: + type: integer + timeout: + type: integer + targetPort: + type: integer + name: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + reference: + type: string + enum: [ bigip ] + send: + type: string + recv: + type: string + sslProfile: + type: string + pattern: '^\/([A-z0-9-_+]+\/)+([A-z0-9]+\/?)*$' + reference: + type: string + enum: [ bigip, service ] + reselectTries: + type: integer + minimum: 0 + maximum: 65535 + serviceDownAction: + type: string + required: + - reference + pools: + type: array + items: + type: object + properties: + name: + type: string + pattern: '^[a-zA-Z]+([-A-z0-9_.+:])*([A-z0-9])+$' + path: + type: string + pattern: '^\/([A-z0-9-_+]+\/)*([-A-z0-9_.:]+\/?)*$' + service: + type: string + pattern: '[a-z]([-a-z0-9]*[a-z0-9])?' + weight: + type: integer + minimum: 0 + maximum: 256 + alternateBackends: + type: array + items: + type: object + properties: + service: + type: string + pattern: '[a-z]([-a-z0-9]*[a-z0-9])?' + serviceNamespace: + type: string + pattern: '^[a-zA-Z]+([-A-z0-9_.+:])*([A-z0-9])+$' + weight: + type: integer + minimum: 0 + maximum: 256 + required: + - service + loadBalancingMethod: + type: string + pattern: '^[a-z]+[a-z_-]+[a-z]+$' + nodeMemberLabel: + type: string + pattern: '^[a-zA-Z0-9][-A-Za-z0-9_.\/]{0,61}[a-zA-Z0-9]=[a-zA-Z0-9][-A-Za-z0-9_.]{0,61}[a-zA-Z0-9]$' + servicePort: + x-kubernetes-int-or-string: true + anyOf: + - type: integer + - type: string + rewrite: + type: string + pattern: '^\/([A-z0-9-_+]+\/)*([-A-z0-9_.:]+\/?)*$' + hostRewrite: + type: string + pattern: '^(([a-zA-Z0-9\*]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$' + waf: + type: string + pattern: '^\/([A-z0-9-_+]+\/)+([A-z0-9]+\/?)*$' + serviceNamespace: + type: string + pattern: '^[a-zA-Z]+([-A-z0-9_.+:])*([A-z0-9])+$' + monitor: + type: object + properties: + type: + type: string + enum: [http, https, tcp] + send: + type: string + recv: + type: string + interval: + type: integer + timeout: + type: integer + targetPort: + type: integer + name: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + reference: + type: string + enum: [bigip] + sslProfile: + type: string + pattern: '^\/([A-z0-9-_+]+\/)+([A-z0-9]+\/?)*$' + monitors: + type: array + items: + type: object + properties: + type: + type: string + enum: [ http, https, tcp ] + send: + type: string + recv: + type: string + interval: + type: integer + timeout: + type: integer + targetPort: + type: integer + name: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + reference: + type: string + enum: [bigip] + sslProfile: + type: string + pattern: '^\/([A-z0-9-_+]+\/)+([A-z0-9]+\/?)*$' + minimumMonitors: + x-kubernetes-int-or-string: true + anyOf: + - type: integer + - type: string + reselectTries: + type: integer + minimum: 0 + maximum: 65535 + serviceDownAction: + type: string + extendedServiceReferences: + type: array + items: + type: object + properties: + clusterName: + type: string + service: + type: string + pattern: '[a-z]([-a-z0-9]*[a-z0-9])?' + namespace: + type: string + servicePort: + x-kubernetes-int-or-string: true + anyOf: + - type: integer + - type: string + weight: + type: integer + minimum: 0 + maximum: 256 + required: + - service + - servicePort + virtualServerAddress: + type: string + pattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])|(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$' + additionalVirtualServerAddresses: + type: array + items: + type: string + pattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])|(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$' + virtualServerName: + type: string + pattern: '^[a-zA-Z]+([A-z0-9-_+])*([A-z0-9])$' + virtualServerHTTPPort: + type: integer + minimum: 1 + maximum: 65535 + virtualServerHTTPSPort: + type: integer + minimum: 1 + maximum: 65535 + x-kubernetes-validations: + - rule: "!has(self.partition) || self.partition != 'Common'" + message: "The partition cannot be 'Common' if specified." + status: + type: object + properties: + vsAddress: + type: string + default: None + status: + type: string + default: Pending + lastUpdated: + type: string + error: + type: string + additionalPrinterColumns: + - name: host + type: string + description: hostname + jsonPath: .spec.host + - name: tlsProfileName + type: string + description: TLS Profile attached + jsonPath: .spec.tlsProfileName + - name: httpTraffic + type: string + description: Http Traffic Termination + jsonPath: .spec.httpTraffic + - name: IPAddress + type: string + description: IP address of virtualServer + jsonPath: .spec.virtualServerAddress + - name: ipamLabel + type: string + description: ipamLabel for virtual server + jsonPath: .spec.ipamLabel + - name: IPAMVSAddress + type: string + description: IP address of virtualServer + jsonPath: .status.vsAddress + - name: STATUS + type: string + description: status of VirtualServer + jsonPath: .status.status + - name: Age + type: date + jsonPath: .metadata.creationTimestamp + subresources: + status: {} + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + labels: + app.kubernetes.io/instance: f5-bigip-ctlr + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: f5-bigip-ctlr + name: tlsprofiles.cis.f5.com +spec: + group: cis.f5.com + names: + kind: TLSProfile + plural: tlsprofiles + shortNames: + - tls + singular: tlsprofile + scope: Namespaced + versions: + - + name: v1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + tlsCipher: + type: object + properties: + tlsVersion: + type: string + enum: ["1.0", "1.1", "1.2", "1.3"] + disableTLSVersions: + type: array + items: + type: string + enum: ["1.0", "1.1", "1.2", "1.3"] + ciphers: + type: string + cipherGroup: + type: string + hosts: + type: array + items: + type: string + pattern: '^(([a-zA-Z0-9\*]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$' + tls: + type: object + properties: + termination: + type: string + enum: [edge, reencrypt, passthrough] + clientSSL: + type: string + pattern: '^\/?[a-zA-Z]+([-A-z0-9_+]+\/)*([-A-z0-9_.:]+\/?)*$' + clientSSLs: + type: array + items: + type: string + pattern: '^\/?[a-zA-Z]+([-A-z0-9_+]+\/)*([-A-z0-9_.:]+\/?)*$' + serverSSL: + type: string + pattern: '^\/?[a-zA-Z]+([-A-z0-9_+]+\/)*([-A-z0-9_.:]+\/?)*$' + serverSSLs: + type: array + items: + type: string + pattern: '^\/?[a-zA-Z]+([-A-z0-9_+]+\/)*([-A-z0-9_.:]+\/?)*$' + reference: + type: string + enum: [bigip, secret, hybrid] + clientSSLParams: + type: object + properties: + renegotiationEnabled: + type: boolean + default: true + profileReference: + type: string + enum: [ bigip, secret ] + serverSSLParams: + type: object + properties: + renegotiationEnabled: + type: boolean + default: true + profileReference: + type: string + enum: [ bigip, secret ] + required: + - termination + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + labels: + app.kubernetes.io/instance: f5-bigip-ctlr + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: f5-bigip-ctlr + name: transportservers.cis.f5.com +spec: + group: cis.f5.com + names: + kind: TransportServer + plural: transportservers + shortNames: + - ts + singular: transportserver + scope: Namespaced + versions: + - + name: v1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + partition: + type: string + pattern: '^[a-zA-Z]+[-A-z0-9_.]+$' + virtualServerAddress: + type: string + pattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])|(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$' + virtualServerPort: + type: integer + minimum: 1 + maximum: 65535 + virtualServerName: + type: string + pattern: '^[a-zA-Z]+([A-z0-9-_+])*([A-z0-9])$' + host: + type: string + pattern: '^(([a-zA-Z0-9\*]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$' + hostGroup: + type: string + pattern: '^[a-zA-Z]+[-A-z0-9_.:]*[A-z0-9]*$' + policyName: + type: string + pattern: '^([A-z0-9-_+])*([A-z0-9])$' + mode: + type: string + enum: [standard, performance] + type: + type: string + enum: [tcp, udp, sctp] + snat: + type: string + pattern: '^$|^\/?[a-zA-Z]+([-A-z0-9_+]+\/)*([-A-z0-9_.:]+\/?)+$' + connectionMirroring: + type: string + enum: [ none, L4 ] + profiles: + type: object + properties: + tcp: + type: object + properties: + client: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + server: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + persistenceProfile: + type: string + pattern: '^\/?[a-zA-Z]+([-A-z0-9_+]+\/)*([-A-z0-9_.:]+\/?)*$' + dos: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + profileL4: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + allowVlans: + items: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.]+\/?)*$' + type: array + iRules: + type: array + items: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + ipamLabel: + type: string + pattern: '^[a-zA-Z]+[-A-z0-9_.:]+[A-z0-9]+$' + bigipRouteDomain: + type: integer + minimum: 0 + maximum: 65535 + default: 0 + serviceAddress: + type: array + maxItems: 1 + items: + type: object + properties: + arpEnabled: + type: boolean + icmpEcho: + type: string + enum: [enable, disable, selective] + routeAdvertisement: + type: string + enum: [enable, disable, selective, always, any, all] + spanningEnabled: + type: boolean + trafficGroup: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + pool: + type: object + properties: + name: + type: string + pattern: '^[a-zA-Z]+([-A-z0-9_.+:])*([A-z0-9])+$' + service: + type: string + pattern: '[a-z]([-a-z0-9]*[a-z0-9])?' + servicePort: + x-kubernetes-int-or-string: true + anyOf: + - type: integer + - type: string + weight: + type: integer + minimum: 0 + maximum: 100 + alternateBackends: + type: array + items: + type: object + properties: + service: + type: string + pattern: '[a-z]([-a-z0-9]*[a-z0-9])?' + serviceNamespace: + type: string + pattern: '^[a-zA-Z]+([-A-z0-9_.+:])*([A-z0-9])+$' + weight: + type: integer + minimum: 0 + maximum: 100 + required: + - service + serviceNamespace: + type: string + pattern: '^[a-zA-Z]+([-A-z0-9_.+:])*([A-z0-9])+$' + loadBalancingMethod: + type: string + pattern: '^[a-z]+[a-z_-]+[a-z]+$' + nodeMemberLabel: + type: string + pattern: '^[a-zA-Z0-9][-A-Za-z0-9_.\/]{0,61}[a-zA-Z0-9]=[a-zA-Z0-9][-A-Za-z0-9_.]{0,61}[a-zA-Z0-9]$' + monitor: + type: object + properties: + type: + type: string + enum: [tcp, udp, http, https] + interval: + type: integer + timeout: + type: integer + targetPort: + type: integer + name: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + reference: + type: string + enum: [bigip] + send: + type: string + recv: + type: string + monitors: + type: array + items: + type: object + properties: + type: + type: string + enum: [ tcp, udp, http, https ] + interval: + type: integer + timeout: + type: integer + targetPort: + type: integer + name: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + reference: + type: string + enum: [bigip] + send: + type: string + recv: + type: string + reselectTries: + type: integer + minimum: 0 + maximum: 65535 + serviceDownAction: + type: string + extendedServiceReferences: + type: array + items: + type: object + properties: + clusterName: + type: string + service: + type: string + pattern: '[a-z]([-a-z0-9]*[a-z0-9])?' + namespace: + type: string + servicePort: + x-kubernetes-int-or-string: true + anyOf: + - type: integer + - type: string + weight: + type: integer + minimum: 0 + maximum: 100 + required: + - service + - servicePort + required: + - virtualServerPort + - pool + - mode + x-kubernetes-validations: + - rule: "!has(self.partition) || self.partition != 'Common'" + message: "The partition cannot be 'Common' if specified." + status: + type: object + properties: + vsAddress: + type: string + default: None + status: + type: string + default: Pending + lastUpdated: + type: string + error: + type: string + additionalPrinterColumns: + - name: virtualServerAddress + type: string + description: IP address of virtualServer + jsonPath: .spec.virtualServerAddress + - name: virtualServerPort + type: integer + description: Port of virtualServer + jsonPath: .spec.virtualServerPort + - name: pool + type: string + description: Name of service + jsonPath: .spec.pool.service + - name: poolPort + type: string + description: Port of service + jsonPath: .spec.pool.servicePort + - name: ipamLabel + type: string + description: ipamLabel for transport server + jsonPath: .spec.ipamLabel + - name: IPAMVSAddress + type: string + description: IP address of transport server + jsonPath: .status.vsAddress + - name: STATUS + type: string + description: status of TransportServer + jsonPath: .status.status + - name: Age + type: date + jsonPath: .metadata.creationTimestamp + subresources: + status: { } +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + labels: + app.kubernetes.io/instance: f5-bigip-ctlr + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: f5-bigip-ctlr + name: externaldnses.cis.f5.com +spec: + group: cis.f5.com + names: + kind: ExternalDNS + plural: externaldnses + shortNames: + - edns + singular: externaldns + scope: Namespaced + versions: + - + name: v1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + domainName: + type: string + pattern: '^(([a-zA-Z0-9\*]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$' + dnsRecordType: + type: string + pattern: 'A' + loadBalanceMethod: + type: string + pattern: '^[a-z]+[a-z_-]+[a-z]+$' + clientSubnetPreferred: + type: boolean + persistenceEnabled: + type: boolean + persistCidrIpv4: + type: integer + minimum: 0 + maximum: 32 + persistCidrIpv6: + type: integer + minimum: 0 + maximum: 128 + ttlPersistence: + type: integer + format: int64 + minimum: 0 + maximum: 4294967295 + pools: + type: array + items: + type: object + properties: + dataServerName: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + dnsRecordType: + type: string + pattern: 'A' + loadBalanceMethod: + type: string + pattern: '^[a-z]+[a-z_-]+[a-z]+$' + lbModeFallback: + type: string + pattern: '^[a-z]+[a-z_-]+[a-z]+$' + order: + type: integer + ratio: + type: integer + monitor: + type: object + properties: + type: + type: string + enum: [http, https, tcp] + send: + type: string + recv: + type: string + interval: + type: integer + timeout: + type: integer + required: + - type + - interval + monitors: + type: array + items: + type: object + properties: + type: + type: string + enum: [http, https, tcp] + send: + type: string + recv: + type: string + interval: + type: integer + timeout: + type: integer + required: + - type + - interval + required: + - dataServerName + required: + - domainName + additionalPrinterColumns: + - name: domainName + type: string + description: Domain name of virtual server resource + jsonPath: .spec.domainName + - name: Age + type: date + jsonPath: .metadata.creationTimestamp + - name: CREATED ON + type: string + jsonPath: .metadata.creationTimestamp +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + labels: + app.kubernetes.io/instance: f5-bigip-ctlr + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: f5-bigip-ctlr + name: ingresslinks.cis.f5.com +spec: + group: cis.f5.com + names: + kind: IngressLink + shortNames: + - il + singular: ingresslink + plural: ingresslinks + scope: Namespaced + versions: + - + name: v1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + partition: + type: string + pattern: '^[a-zA-Z]+[-A-z0-9_.]+$' + virtualServerAddress: + type: string + pattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$' + host: + type: string + pattern: '^(([a-zA-Z0-9\*]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$' + ipamLabel: + type: string + pattern: '^[a-zA-Z]+[-A-z0-9_.:]+[A-z0-9]+$' + bigipRouteDomain: + type: integer + minimum: 0 + maximum: 65535 + default: 0 + iRules: + type: array + items: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + selector: + properties: + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-validations: + - rule: "!has(self.partition) || self.partition != 'Common'" + message: "The partition cannot be 'Common' if specified." + status: + type: object + properties: + vsAddress: + type: string + status: + type: string + default: pending + lastUpdated: + type: string + error: + type: string + additionalPrinterColumns: + - name: IPAMVSAddress + type: string + description: IP address of virtualServer + jsonPath: .status.vsAddress + - name: Age + type: date + jsonPath: .metadata.creationTimestamp + subresources: + status: { } +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + labels: + app.kubernetes.io/instance: f5-bigip-ctlr + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: f5-bigip-ctlr + name: policies.cis.f5.com +spec: + group: cis.f5.com + names: + kind: Policy + shortNames: + - plc + singular: policy + plural: policies + scope: Namespaced + versions: + - + name: v1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + l7Policies: + type: object + properties: + waf: + type: string + pattern: '^\/([A-z0-9-_+]+\/)+([A-z0-9]+\/?)*$' + profileAccess: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + policyPerRequestAccess: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + profileAdapt: + type: object + properties: + request: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + response: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + l3Policies: + type: object + properties: + dos: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + botDefense: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + firewallPolicy: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([A-z0-9]+\/?)*$' + ipIntelligencePolicy: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + allowSourceRange: + items: + type: string + type: array + allowVlans: + items: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)*([A-z0-9-_.\s]+\/?)*$' + type: array + ltmPolicies: + type: object + properties: + insecure: + type: string + pattern: '^\/[a-zA-Z]+([-A-z0-9_+:]+\/)+([A-z0-9]+\/?)*$' + secure: + type: string + pattern: '^\/[a-zA-Z]+([-A-z0-9_+:]+\/)+([A-z0-9]+\/?)*$' + priority: + type: string + enum: [low, high] + iRules: + type: object + properties: + insecure: + type: string + pattern: '^none$|^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + secure: + type: string + pattern: '^none$|^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + priority: + type: string + enum: [ low, high ] + iRuleList: + type: array + items: + type: string + pattern: '^none$|^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + defaultPool: + type: object + properties: + name: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + service: + type: string + pattern: '[a-z]([-a-z0-9]*[a-z0-9])?' + servicePort: + x-kubernetes-int-or-string: true + anyOf: + - type: integer + - type: string + serviceNamespace: + type: string + pattern: '^[a-zA-Z]+([-A-z0-9_.+:])*([A-z0-9])+$' + loadBalancingMethod: + type: string + pattern: '^[a-z]+[a-z_-]+[a-z]+$' + nodeMemberLabel: + type: string + pattern: '^[a-zA-Z0-9][-A-Za-z0-9_.\/]{0,61}[a-zA-Z0-9]=[a-zA-Z0-9][-A-Za-z0-9_.]{0,61}[a-zA-Z0-9]$' + monitors: + type: array + items: + type: object + properties: + type: + type: string + enum: [ tcp, udp, http, https ] + interval: + type: integer + timeout: + type: integer + targetPort: + type: integer + name: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + reference: + type: string + enum: [ bigip ] + send: + type: string + recv: + type: string + sslProfile: + type: string + pattern: '^\/([A-z0-9-_+]+\/)+([A-z0-9]+\/?)*$' + reference: + type: string + enum: [ bigip, service ] + reselectTries: + type: integer + minimum: 0 + maximum: 65535 + serviceDownAction: + type: string + required: + - reference + profiles: + type: object + properties: + tcp: + type: object + properties: + client: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + server: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + udp: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + http: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + http2: + type: object + properties: + client: + type: string + pattern: '^\/([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + server: + type: string + pattern: '^\/([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + persistenceProfile: + type: string + pattern: '^\/?[a-zA-Z]+([-A-z0-9_+]+\/)*([-A-z0-9_.:]+\/?)*$' + profileL4: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + profileWebSocket: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + profileMultiplex: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + rewriteProfile: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([A-z0-9]+\/?)*$' + logProfiles: + items: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)*([-A-z0-9._\s]+\/?)*$' + type: array + httpMrfRoutingEnabled: + type: boolean + sslProfiles: + type: object + properties: + clientProfiles: + items: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + type: array + serverProfiles: + items: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + type: array + analyticsProfiles: + type: object + properties: + http: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + htmlProfile: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + ftpProfile: + type: string + pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$' + autoLastHop: + type: string + enum: [ default, auto, disable ] + snat: + type: string + pattern: '^$|^\/?[a-zA-Z]+([-A-z0-9_+]+\/)*([-A-z0-9_.:]+\/?)+$' + poolSettings: + type: object + properties: + reselectTries: + type: integer + minimum: 0 + maximum: 65535 + serviceDownAction: + type: string + slowRampTime: + type: integer + minimum: 0 + maximum: 900 + multiPoolPersistence: + type: object + properties: + method: + type: string + enum: [ uieSourceAddress, hashSourceAddress ] + timeOut: + type: integer + minimum: 1 + default: 180 diff --git a/charts/f5/f5-bigip-ctlr/0.0.3301/questions.yaml b/charts/f5/f5-bigip-ctlr/0.0.3301/questions.yaml new file mode 100644 index 000000000..4d276dfbd --- /dev/null +++ b/charts/f5/f5-bigip-ctlr/0.0.3301/questions.yaml @@ -0,0 +1,75 @@ +questions: +- variable: bigip_login_secret + required: true + type: string + label: "Name of the k8s secret object with BIG-IP login credentials." +- variable: args.bigip_url + required: true + type: string + label: "BIG-IP Management IP/URL" +- variable: args.bigip_partition + required: true + type: string + label: "BIG-IP Partition" +- variable: image.user + type: string + label: "Image Repository where CIS image is hosted" +- variable: image.repo + type: string + label: "CIS image name" +- variable: version + type: string + label: "CIS version tag." + default: "latest" +- variable: args.pool_member_type + type: string + label: "Type of BIG-IP Pool members to create." + default: "nodeport" +- variable: args.node_poll_interval + type: string + label: "In seconds, the interval at which the CIS polls the cluster to find all node members." + default: "30" +- variable: args.verify_interval + type: string + label: "In seconds, the interval at which the CIS verifies that the BIG-IP configuration matches the state of the orchestration system." + default: "30" +- variable: args.agent + type: string + label: "Specify the agent for CIS to communicate with BIG-IP. CCCL or AS3" + default: "as3" +- variable: args.custom_resource_mode + type: string + label: "Set 'true' to process CRD resources. Supported in AS3 agent. When true ConfigMaps, Routes, and Ingress are not processed by CIS." + default: "false" +- variable: args.ipam + type: string + label: "Specify if CIS provides the ability to interface with F5 IPAM Controller (FIC). Valid with agent AS3." + default: "false" +- variable: args.disable_teems + type: string + label: "If true, analytics data is not sent to F5." + default: "false" +- variable: args.hubmode + type: string + label: "When `true`, ConfigMaps with Services in same and different namespace are processed. CIS >= 2.5.0+. Valid with agent AS3." + default: "false" +- variable: args.default_route_domain + type: string + label: "Set default Route Domain for Custom resources. Valid with agent AS3." + default: "0" +- variable: args.filter_tenants + type: string + label: "Specify to use tenant filtering API for AS3 declaration. This allows CIS to process each AS3 Tenant separately. Compatible with ConfigMap only. Valid with agent AS3. CIS >= 2.7" + default: "false" +- variable: args.enable_ipv6 + type: string + label: "When set to true, it enables IPv6 network support. CIS >= 2.7." + default: "false" +- variable: args.log_level + type: string + label: "Configured the log level. INFO, DEBUG, CRITICAL, WARNING, ERROR." + default: "INFO" +- variable: args.log_as3_response + type: string + label: "When set to true, adds the body of AS3 API response in Controller logs." + default: "false" diff --git a/charts/f5/f5-bigip-ctlr/0.0.3301/templates/NOTES.txt b/charts/f5/f5-bigip-ctlr/0.0.3301/templates/NOTES.txt new file mode 100644 index 000000000..302512a9b --- /dev/null +++ b/charts/f5/f5-bigip-ctlr/0.0.3301/templates/NOTES.txt @@ -0,0 +1,6 @@ +Container Ingress Services controller: {{ .Release.Name }} + +Controller Documentation: +- Kubernetes: https://clouddocs.f5.com/containers/latest/userguide/kubernetes/ +- OpenShift: https://clouddocs.f5.com/containers/latest/userguide/openshift/ + diff --git a/charts/f5/f5-bigip-ctlr/0.0.3301/templates/_helpers.tpl b/charts/f5/f5-bigip-ctlr/0.0.3301/templates/_helpers.tpl new file mode 100644 index 000000000..7ce05d2ef --- /dev/null +++ b/charts/f5/f5-bigip-ctlr/0.0.3301/templates/_helpers.tpl @@ -0,0 +1,64 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "f5-bigip-ctlr.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for deployment. +*/}} +{{- define "deployment.apiVersion" -}} +{{- if semverCompare ">=1.9-0" .Capabilities.KubeVersion.GitVersion -}} +{{- print "apps/v1" -}} +{{- else -}} +{{- print "extensions/v1beta1" -}} +{{- end -}} +{{- end -}} + +{{/* +Check for user given namespace or give kube-system +*/}} +{{- define "f5-bigip-ctlr.namespace" -}} +{{- if hasKey .Values "namespace" -}} +{{- .Values.namespace -}} +{{- else -}} +{{- print "kube-system" -}} +{{- end -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "f5-bigip-ctlr.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 "f5-bigip-ctlr.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + + {{/* +Create the name of the service account to use +*/}} +{{- define "f5-bigip-ctlr.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} + {{ default (include "f5-bigip-ctlr.fullname" .) .Values.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} diff --git a/charts/f5/f5-bigip-ctlr/0.0.3301/templates/f5-bigip-ctlr-clusterrole.yaml b/charts/f5/f5-bigip-ctlr/0.0.3301/templates/f5-bigip-ctlr-clusterrole.yaml new file mode 100644 index 000000000..c60eb6eb8 --- /dev/null +++ b/charts/f5/f5-bigip-ctlr/0.0.3301/templates/f5-bigip-ctlr-clusterrole.yaml @@ -0,0 +1,111 @@ +{{- if .Values.rbac.create -}} +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ template "f5-bigip-ctlr.fullname" . }} + labels: + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/name: {{ template "f5-bigip-ctlr.name" . }} + app: {{ template "f5-bigip-ctlr.name" . }} + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +rules: + - apiGroups: + - '' + - extensions + - networking.k8s.io + - route.openshift.io + resources: + - nodes + - services + - endpoints + - namespaces + - ingresses + - pods + - ingressclasses + - policies + - routes + verbs: + - get + - list + - watch + - apiGroups: + - '' + - extensions + - networking.k8s.io + - route.openshift.io + resources: + - configmaps + - events + - ingresses/status + - services/status + - routes/status + verbs: + - get + - list + - watch + - update + - create + - patch + - apiGroups: + - cis.f5.com + resources: + - virtualservers + - virtualservers/status + - tlsprofiles + - transportservers + - transportservers/status + - ingresslinks + - ingresslinks/status + - externaldnses + - policies + verbs: + - get + - list + - watch + - update + - patch + - apiGroups: + - '' + - extensions + resources: + - secrets + verbs: + - get + - list + - watch + - apiGroups: + - config.openshift.io/v1 + resources: + - network + verbs: + - list +{{- if .Values.args.ipam }} + - apiGroups: + - fic.f5.com + resources: + - ipams + - ipams/status + verbs: + - get + - list + - watch + - update + - create + - patch + - delete + - apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - get + - list + - watch + - update + - create + - patch +{{- end }} +{{- end }} diff --git a/charts/f5/f5-bigip-ctlr/0.0.3301/templates/f5-bigip-ctlr-clusterrolebinding.yaml b/charts/f5/f5-bigip-ctlr/0.0.3301/templates/f5-bigip-ctlr-clusterrolebinding.yaml new file mode 100644 index 000000000..aba54704d --- /dev/null +++ b/charts/f5/f5-bigip-ctlr/0.0.3301/templates/f5-bigip-ctlr-clusterrolebinding.yaml @@ -0,0 +1,23 @@ +{{- if .Values.rbac.create -}} +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ template "f5-bigip-ctlr.fullname" . }} + namespace: {{ template "f5-bigip-ctlr.namespace" . }} + labels: + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/name: {{ template "f5-bigip-ctlr.name" . }} + app: {{ template "f5-bigip-ctlr.name" . }} + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "f5-bigip-ctlr.fullname" . }} +subjects: +- kind: ServiceAccount + name: {{ template "f5-bigip-ctlr.serviceAccountName" . }} + namespace: {{ template "f5-bigip-ctlr.namespace" . }} +{{- end -}} diff --git a/charts/f5/f5-bigip-ctlr/0.0.3301/templates/f5-bigip-ctlr-deploy.yaml b/charts/f5/f5-bigip-ctlr/0.0.3301/templates/f5-bigip-ctlr-deploy.yaml new file mode 100644 index 000000000..1a477cd78 --- /dev/null +++ b/charts/f5/f5-bigip-ctlr/0.0.3301/templates/f5-bigip-ctlr-deploy.yaml @@ -0,0 +1,139 @@ +{{- if or (not .Values.args.bigip_url) (not .Values.args.bigip_partition) }} +{{/* +Generate errors for missing required values. +*/}} +# {{required "BIG-IP url not specified - add to Values or pass with `--set` " .Values.args.bigip_url }} +# {{required "BIG-IP partition not specified - add to Values or pass with `--set` " .Values.args.bigip_partition }} +{{- else -}} +apiVersion: {{ template "deployment.apiVersion" . }} +kind: Deployment +metadata: + name: {{ template "f5-bigip-ctlr.fullname" . }} + namespace: {{ template "f5-bigip-ctlr.namespace" . }} + labels: + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/name: {{ template "f5-bigip-ctlr.name" . }} + app: {{ template "f5-bigip-ctlr.name" . }} + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "-" }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: 1 + selector: + matchLabels: + app: {{ template "f5-bigip-ctlr.name" . }} + template: + metadata: + labels: + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/name: {{ template "f5-bigip-ctlr.name" . }} + app: {{ template "f5-bigip-ctlr.name" . }} + release: {{ .Release.Name }} + spec: +{{- if .Values.affinity }} + affinity: +{{ toYaml .Values.affinity | indent 8 }} +{{- end }} + serviceAccountName: {{ template "f5-bigip-ctlr.serviceAccountName" . }} +{{- if .Values.image.pullSecrets }} + imagePullSecrets: + {{- range $pullSecret := .Values.image.pullSecrets }} + - name: {{ $pullSecret }} + {{- end }} +{{- end }} + securityContext: + {{- $securityContext := .Values.securityContext | default dict }} + {{- $oscpOperator := .Files.Glob "oscp-operator.txt" }} + {{- if $securityContext.runAsUser }} + runAsUser: {{ $securityContext.runAsUser }} + {{- else if (not $oscpOperator) }} + runAsUser: 1000 + {{- end }} + {{- $securityContext := .Values.securityContext | default dict }} + {{- if $securityContext.runAsGroup }} + runAsGroup: {{ $securityContext.runAsGroup }} + {{- else if (not $oscpOperator) }} + runAsGroup: 1000 + {{- end }} + {{- $securityContext := .Values.securityContext | default dict }} + {{- if $securityContext.fsGroup }} + fsGroup: {{ $securityContext.fsGroup }} + {{- else if (not $oscpOperator) }} + fsGroup: 1000 + {{- end }} + containers: + - name: {{ template "f5-bigip-ctlr.name" . }} + image: "{{ .Values.image.user }}/{{ .Values.image.repo }}:{{ .Values.version }}" + {{- if .Values.podSecurityContext }} + securityContext: +{{ toYaml .Values.podSecurityContext | indent 12 }} + {{- end }} + livenessProbe: + failureThreshold: 3 + httpGet: + path: /health + port: 8080 + scheme: HTTP + initialDelaySeconds: 15 + periodSeconds: 15 + successThreshold: 1 + timeoutSeconds: 15 + readinessProbe: + failureThreshold: 3 + httpGet: + path: /health + port: 8080 + scheme: HTTP + initialDelaySeconds: 30 + periodSeconds: 30 + successThreshold: 1 + timeoutSeconds: 15 + volumeMounts: + - name: bigip-creds + mountPath: "/tmp/creds" + readOnly: true + imagePullPolicy: {{ .Values.image.pullPolicy }} + command: + - /app/bin/k8s-bigip-ctlr + args: +{{- if .Values.ingressClass.ingressClassName }} + - --ingress-class={{ .Values.ingressClass.ingressClassName | default "f5" }} +{{- end }} + - --credentials-directory + - /tmp/creds + {{- $ns := .Values.args.namespaces }} + {{- range $key, $value := .Values.args }} + {{- if eq $key "namespaces" }} + {{- range $ns}} + - --namespace={{ . }} + {{- end }} + {{- else }} + - --{{ $key | replace "_" "-"}}={{ $value }} + {{- end }} + {{- end }} + resources: + limits: + cpu: {{ .Values.limits_cpu | default "100m" }} + memory: {{ .Values.limits_memory | default "512Mi" }} + requests: + cpu: {{ .Values.requests_cpu | default "100m" }} + memory: {{ .Values.requests_memory | default "512Mi" }} +{{- if .Values.nodeSelector }} + nodeSelector: +{{ toYaml .Values.nodeSelector | indent 8 }} +{{- end }} +{{- if .Values.tolerations }} + tolerations: +{{ toYaml .Values.tolerations | indent 6}} +{{- end }} + volumes: + - name: bigip-creds + secret: + {{- if .Values.bigip_secret.create }} + secretName: f5-bigip-ctlr-login + {{- else }} + secretName: {{ .Values.bigip_login_secret }} + {{- end }} +{{- end }} diff --git a/charts/f5/f5-bigip-ctlr/0.0.3301/templates/f5-bigip-ctlr-ingress-class.yaml b/charts/f5/f5-bigip-ctlr/0.0.3301/templates/f5-bigip-ctlr-ingress-class.yaml new file mode 100644 index 000000000..2105a219a --- /dev/null +++ b/charts/f5/f5-bigip-ctlr/0.0.3301/templates/f5-bigip-ctlr-ingress-class.yaml @@ -0,0 +1,12 @@ +{{- if .Values.ingressClass.create -}} + +apiVersion: networking.k8s.io/v1 +kind: IngressClass +metadata: + name: {{ .Values.ingressClass.ingressClassName | default "f5" }} + annotations: + ingressclass.kubernetes.io/is-default-class: "{{ .Values.ingressClass.isDefaultIngressController | default false }}" +spec: + controller: f5.com/cntr-ingress-svcs + +{{- end -}} diff --git a/charts/f5/f5-bigip-ctlr/0.0.3301/templates/f5-bigip-ctlr-secrets.yaml b/charts/f5/f5-bigip-ctlr/0.0.3301/templates/f5-bigip-ctlr-secrets.yaml new file mode 100644 index 000000000..181415835 --- /dev/null +++ b/charts/f5/f5-bigip-ctlr/0.0.3301/templates/f5-bigip-ctlr-secrets.yaml @@ -0,0 +1,19 @@ +{{- if .Values.bigip_secret.create -}} +apiVersion: v1 +kind: Secret +metadata: + name: f5-bigip-ctlr-login + namespace: {{ template "f5-bigip-ctlr.namespace" . }} + labels: + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/name: {{ template "f5-bigip-ctlr.name" . }} + app: {{ template "f5-bigip-ctlr.name" . }} + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +type: Opaque +data: + username: {{ .Values.bigip_secret.username | b64enc | quote }} + password: {{ .Values.bigip_secret.password | b64enc | quote }} +{{- end -}} \ No newline at end of file diff --git a/charts/f5/f5-bigip-ctlr/0.0.3301/templates/f5-bigip-ctlr-serviceaccount.yaml b/charts/f5/f5-bigip-ctlr/0.0.3301/templates/f5-bigip-ctlr-serviceaccount.yaml new file mode 100644 index 000000000..5729a8bc1 --- /dev/null +++ b/charts/f5/f5-bigip-ctlr/0.0.3301/templates/f5-bigip-ctlr-serviceaccount.yaml @@ -0,0 +1,17 @@ +{{- if .Values.rbac.create -}} +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "f5-bigip-ctlr.serviceAccountName" . }} + namespace: {{ template "f5-bigip-ctlr.namespace" . }} + labels: + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/name: {{ template "f5-bigip-ctlr.name" . }} + app: {{ template "f5-bigip-ctlr.name" . }} + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +{{- end -}} +{{- end -}} diff --git a/charts/f5/f5-bigip-ctlr/0.0.3301/values.yaml b/charts/f5/f5-bigip-ctlr/0.0.3301/values.yaml new file mode 100644 index 000000000..d9952d45a --- /dev/null +++ b/charts/f5/f5-bigip-ctlr/0.0.3301/values.yaml @@ -0,0 +1,90 @@ +# For additional information on installing the k8-bigip-ctlr please see: +# Kubernetes: https://clouddocs.f5.com/containers/latest/userguide/kubernetes/#cis-installation +# OpenShift: https://clouddocs.f5.com/containers/latest/userguide/openshift/#cis-installation +# +# access / permissions / RBAC +# To create a secret using kubectl see +# https://clouddocs.f5.com/containers/latest/userguide/kubernetes/#installing-cis-manually +bigip_login_secret: f5-bigip-ctlr-login + +bigip_secret: + create: false + username: + password: + +rbac: + create: true +serviceAccount: + # Specifies whether a service account should be created + create: true + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: f5-bigip-ctlr-serviceaccount +# This namespace is where the Controller lives; +namespace: kube-system + +ingressClass: + create: true + ingressClassName: f5 + isDefaultIngressController: true +args: + # See https://clouddocs.f5.com/containers/latest/userguide/config-parameters.html + # NOTE: helm has difficulty with values using `-`; `_` are used for naming + # and are replaced with `-` during rendering. + # REQUIRED Params + bigip_url: ~ + bigip_partition: f5-bigip-ctlr + # OPTIONAL PARAMS -- uncomment and provide values for those you wish to use. + # verify_interval: + # node-poll_interval: + # log_level: + # python_basedir: ~ + # VXLAN + # openshift_sdn_name: + # flannel_name: + # KUBERNETES + # default_ingress_ip: + # kubeconfig: + # namespaces: ["foo", "bar"] + # namespace_label: + # node_label_selector: + # pool_member_type: + # resolve_ingress_names: + # running_in_cluster: + # use_node_internal: + # use_secrets: + # insecure: true + # custom-resource-mode: true + # log-as3-response: true + # gtm-bigip-password + # gtm-bigip-url + # gtm-bigip-username + # ipam : true + +image: + # Use the tag to target a specific version of the Controller + user: f5networks + repo: k8s-bigip-ctlr + pullPolicy: Always +version: latest +# affinity: +# nodeAffinity: +# requiredDuringSchedulingIgnoredDuringExecution: +# nodeSelectorTerms: +# - matchExpressions: +# - key: kubernetes.io/arch +# operator: Exists +# securityContext: +# runAsUser: 1000 +# runAsGroup: 3000 +# fsGroup: 2000 +# If you want to specify resources, uncomment the following +# limits_cpu: 100m +# limits_memory: 512Mi +# requests_cpu: 100m +# requests_memory: 512Mi +# Set podSecurityContext for Pod Security Admission and Pod Security Standards +# podSecurityContext: +# runAsUser: 1000 +# runAsGroup: 1000 +# privileged: true diff --git a/charts/percona/pxc-db/1.15.1/.helmignore b/charts/percona/pxc-db/1.15.1/.helmignore new file mode 100644 index 000000000..50af03172 --- /dev/null +++ b/charts/percona/pxc-db/1.15.1/.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/percona/pxc-db/1.15.1/Chart.yaml b/charts/percona/pxc-db/1.15.1/Chart.yaml new file mode 100644 index 000000000..e46bf2ea1 --- /dev/null +++ b/charts/percona/pxc-db/1.15.1/Chart.yaml @@ -0,0 +1,21 @@ +annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Percona XtraDB Cluster + catalog.cattle.io/kube-version: '>=1.21-0' + catalog.cattle.io/release-name: pxc-db +apiVersion: v2 +appVersion: 1.15.1 +description: A Helm chart for installing Percona XtraDB Cluster Databases using the + PXC Operator. +home: https://www.percona.com/doc/kubernetes-operator-for-pxc/kubernetes.html +icon: file://assets/icons/pxc-db.png +kubeVersion: '>=1.21-0' +maintainers: +- email: tomislav.plavcic@percona.com + name: tplavcic +- email: sergey.pronin@percona.com + name: spron-in +- email: natalia.marukovich@percona.com + name: nmarukovich +name: pxc-db +version: 1.15.1 diff --git a/charts/percona/pxc-db/1.15.1/README.md b/charts/percona/pxc-db/1.15.1/README.md new file mode 100644 index 000000000..08a04306d --- /dev/null +++ b/charts/percona/pxc-db/1.15.1/README.md @@ -0,0 +1,331 @@ +# Percona XtraDB Cluster + +[Percona XtraDB Cluster (PXC)](https://www.percona.com/doc/percona-xtradb-cluster/LATEST/index.html) is a database clustering solution for MySQL. This chart deploys Percona XtraDB Cluster on Kubernetes controlled by Percona Operator for MySQL. + +Useful links +* [Operator Github repository](https://github.com/percona/percona-xtradb-cluster-operator) +* [Operator Documentation](https://www.percona.com/doc/kubernetes-operator-for-pxc/index.html) + +## Pre-requisites +* [Percona Operator for MySQL](https://hub.helm.sh/charts/percona/pxc-operator) running in your Kubernetes cluster. See installation details [here](https://github.com/percona/percona-helm-charts/tree/main/charts/pxc-operator) or in the [Operator Documentation](https://www.percona.com/doc/kubernetes-operator-for-pxc/helm.html). +* Kubernetes 1.28+ + +* Helm v3 + +## Chart Details +This chart will deploy Percona XtraDB Cluster in Kubernetes. It will create a Custom Resource, and the Operator will trigger the creation of corresponding Kubernetes primitives: StatefulSets, Pods, Secrets, etc. + +### Installing the Chart +To install the chart with the `pxc` release name using a dedicated namespace (recommended): + +```sh +helm repo add percona https://percona.github.io/percona-helm-charts/ +helm install my-db percona/pxc-db --version 1.15.1 --namespace my-namespace +``` + +The chart can be customized using the following configurable parameters: + +| Parameter | Description | Default | +| ------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------ | +| `crVersion` | Version of the Operator the Custom Resource belongs to | `1.15.1` | +| `ignoreAnnotations` | Operator will not remove following annotations | `[]` | +| `ignoreLabels` | Operator will not remove following labels | `[]` | +| `pause` | Stop PXC Database safely | `false` | +| `enableVolumeExpansion` | Enable volume resizing | `false` | +| `unsafeFlags.tls` | Allows users to configure a cluster without TLS/SSL certificates | `false` | +| `unsafeFlags.pxcSize` | Allows users to configure a cluster with less than 3 Percona XtraDB Cluster instances | `false` | +| `unsafeFlags.proxySize` | Allows users to configure a cluster with less than 2 ProxySQL or HAProxy Pods | `false` | +| `unsafeFlags.backupIfUnhealthy` | Allows running a backup even if the cluster status is not `ready` | `false` | +| `enableCRValidationWebhook` | Enables or disables schema validation before applying custom resource | `false` | +| `initContainer.image` | An alternative image for the initial Operator installation | `""` | +| `initContainer.resources.requests` | Init container resource requests | `{}` | +| `initContainer.resources.limits` | Init container resource limits | `{}` | +| `updateStrategy` | Regulates the way how PXC Cluster Pods will be updated after setting a new image | `SmartUpdate` | +| `upgradeOptions.versionServiceEndpoint` | Endpoint for actual PXC Versions provider | `https://check.percona.com/versions` | +| `upgradeOptions.apply` | PXC image to apply from version service - `recommended`, `latest`, actual version like `8.0.19-10.1` | `disabled` | +| `upgradeOptions.schedule` | Cron formatted time to execute the update | `"0 4 * * *"` | +| `finalizers:percona.com/delete-pxc-pods-in-order` | Set this if you want to delete PXC pods in order on cluster deletion | [] | +| `finalizers:percona.com/delete-proxysql-pvc` | Set this if you want to delete proxysql persistent volumes on cluster deletion | [] | +| `finalizers:percona.com/delete-pxc-pvc` | Set this if you want to delete database persistent volumes on cluster deletion | [] | +| `finalizers:percona.com/delete-ssl` | Deletes objects created for SSL (Secret, certificate, and issuer) after the cluster deletion | [] | +| `annotations` | PerconaXtraDBCluster custom resource annotations | {} | +| | +| `tls.enabled` | Enable PXC Pod communication with TLS | `true` | +| `tls.SANs` | Additional domains (SAN) to be added to the TLS certificate within the extended cert-manager configuration | `[]` | +| `tls.issuerConf.name` | A cert-manager issuer name | `""` | +| `tls.issuerConf.kind` | A cert-manager issuer type | `""` | +| `tls.issuerConf.group` | A cert-manager issuer group | `""` | +| | +| `pxc.size` | PXC Cluster target member (pod) quantity. Can't even if `unsafeFlags.pxcSize` is `true` | `3` | +| `pxc.clusterSecretName` | Specify if you want to use custom or Operator generated users secret (if the one specified doesn't exist) | `` | +| `pxc.image.repository` | PXC Container image repository | `percona/percona-xtradb-cluster` | +| `pxc.image.tag` | PXC Container image tag | `8.0.36-28.1` | +| `pxc.imagePullPolicy` | The policy used to update images | `` | +| `pxc.autoRecovery` | Enable full cluster crash auto recovery | `true` | +| `pxc.expose.enabled` | Enable or disable exposing `Percona XtraDB Cluster` nodes with dedicated IP addresses | `true` | +| `pxc.expose.type` | The Kubernetes Service Type used for exposure | `LoadBalancer` | +| `pxc.expose.externalTrafficPolicy` | Specifies whether Service for Percona XtraDB Cluster should [route external traffic to cluster-wide or to node-local endpoints](https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip) (it can influence the load balancing effectiveness) | `""` | +| `pxc.expose.internalTrafficPolicy` | Specifies whether Service for Percona XtraDB Cluster should [route internal traffic to cluster-wide or to node-local endpoints](https://kubernetes.io/docs/concepts/services-networking/service-traffic-policy/) (it can influence the load balancing effectiveness) | `""` | +| `pxc.expose.loadBalancerSourceRanges` | The range of client IP addresses from which the load balancer should be reachable (if not set, there is no limitations) | `[]` | +| `pxc.expose.loadBalancerIP` | The static IP-address for the load balancer | `""` | +| `pxc.expose.annotations` | The Kubernetes annotations for exposed service | `{}` | +| `pxc.expose.labels` | The Kubernetes labels for exposed service | `{}` | +| `pxc.replicationChannels.name` | Name of the replication channel for cross-site replication | `pxc1_to_pxc2` | +| `pxc.replicationChannels.isSource` | Should the cluster act as Source (true) or Replica (false) in cross-site replication | `false` | +| `pxc.replicationChannels.sourcesList.host` | For the cross-site replication Replica cluster, this key should contain the hostname or IP address of the Source cluster | `10.95.251.101` | +| `pxc.replicationChannels.sourcesList.port` | For the cross-site replication Replica cluster, this key should contain the Source port number | `3306` | +| `pxc.replicationChannels.sourcesList.weight` | For the cross-site replication Replica cluster, this key should contain the Source cluster weight | `100` | +| `pxc.imagePullSecrets` | PXC Container pull secret | `[]` | +| `pxc.annotations` | PXC Pod user-defined annotations | `{}` | +| `pxc.priorityClassName` | PXC Pod priority Class defined by user | | +| `pxc.runtimeClassName` | Name of the Kubernetes Runtime Class for PXC Pods | | +| `pxc.labels` | PXC Pod user-defined labels | `{}` | +| `pxc.schedulerName` | The Kubernetes Scheduler | | +| `pxc.readinessDelaySec` | PXC Pod delay for readiness probe in seconds | `15` | +| `pxc.livenessDelaySec` | PXC Pod delay for liveness probe in seconds | `300` | +| `pxc.configuration` | User defined MySQL options according to MySQL configuration file syntax | `` | +| `pxc.envVarsSecret` | A secret with environment variables | `` | +| `pxc.resources.requests` | PXC Pods resource requests | `{"memory": "1G", "cpu": "600m"}` | +| `pxc.resources.limits` | PXC Pods resource limits | `{}` | +| `pxc.sidecars` | PXC Pods sidecars | `[]` | +| `pxc.sidecarVolumes` | PXC Pods sidecarVolumes | `[]` | +| `pxc.sidecarPVCs` | PXC Pods sidecar PVCs | `[]` | +| `pxc.sidecarResources.requests` | PXC sidecar resource requests | `{}` | +| `pxc.sidecarResources.limits` | PXC sidecar resource limits | `{}` | +| `pxc.nodeSelector` | PXC Pods key-value pairs setting for K8S node assingment | `{}` | +| `pxc.topologySpreadConstraints` | The Label selector for the [Kubernetes Pod Topology Spread Constraints](https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/) | `[]` | +| `pxc.affinity.antiAffinityTopologyKey` | PXC Pods simple scheduling restriction on/off for host, zone, region | `"kubernetes.io/hostname"` | +| `pxc.affinity.advanced` | PXC Pods advanced scheduling restriction with match expression engine | `{}` | +| `pxc.tolerations` | List of node taints to tolerate for PXC Pods | `[]` | +| `pxc.gracePeriod` | Allowed time for graceful shutdown | `600` | +| `pxc.lifecycle.preStop.exec.command` | Command for the [preStop lifecycle hook](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/) for Percona XtraDB Cluster Pods | `""` | +| `pxc.lifecycle.postStart.exec.command` | Command for the [postStart lifecycle hook](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/) for Percona XtraDB Cluster Pods | `600` | +| `pxc.podDisruptionBudget.maxUnavailable` | Instruct Kubernetes about the failed pods allowed quantity | `1` | +| `pxc.persistence.enabled` | Requests a persistent storage (`hostPath` or `storageClass`) from K8S for PXC Pods datadir | `true` | +| `pxc.persistence.hostPath` | Sets datadir path on K8S node for all PXC Pods. Available only when `pxc.persistence.enabled: true` | | +| `pxc.persistence.storageClass` | Sets K8S storageClass name for all PXC Pods PVC. Available only when `pxc.persistence.enabled: true` | `-` | +| `pxc.persistence.accessMode` | Sets K8S persistent storage access policy for all PXC Pods | `ReadWriteOnce` | +| `pxc.persistence.dataSource.name` | The name of PVC used as a data source to [create the Percona XtraDB Cluster Volumes by cloning :octicons-link-external-16:](https://kubernetes.io/docs/concepts/storage/volume-pvc-datasource/). | `` | +| `pxc.persistence.dataSource.kind` | The [Kubernetes DataSource type :octicons-link-external-16:](https://kubernetes-csi.github.io/docs/volume-datasources.html#supported-datasources). | `` | +| `pxc.persistence.dataSource.apiGroup` | The [Kubernetes API group :octicons-link-external-16:](https://kubernetes.io/docs/reference/using-api/#api-groups) to use for [PVC Data Source :octicons-link-external-16:](https://kubernetes-csi.github.io/docs/volume-datasources.html). | `` | +| `pxc.persistence.size` | Sets K8S persistent storage size for all PXC Pods | `8Gi` | +| `pxc.certManager` | Enable this option if you want the operator to request certificates from `cert-manager` | `false` | +| `pxc.readinessProbes.failureThreshold` | When a probe fails, Kubernetes will try failureThreshold times before giving up | `5` | +| `pxc.readinessProbes.initialDelaySeconds` | Number of seconds after the container has started before liveness or readiness probes are initiated | `15` | +| `pxc.readinessProbes.periodSeconds` | How often (in seconds) to perform the probe | `30` | +| `pxc.readinessProbes.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed | `1` | +| `pxc.readinessProbes.timeoutSeconds` | Number of seconds after which the probe times out | `15` | +| `pxc.livenessProbes.failureThreshold` | When a probe fails, Kubernetes will try failureThreshold times before giving up | `3` | +| `pxc.livenessProbes.initialDelaySeconds` | Number of seconds after the container has started before liveness or readiness probes are initiated | `300` | +| `pxc.livenessProbes.periodSeconds` | How often (in seconds) to perform the probe | `10` | +| `pxc.livenessProbes.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed | `1` | +| `pxc.livenessProbes.timeoutSeconds` | Number of seconds after which the probe times out | `5` | +| `pxc.containerSecurityContext` | A custom Kubernetes Security Context for a Container to be used instead of the default one | `{}` | +| `pxc.podSecurityContext` | A custom Kubernetes Security Context for a Pod to be used instead of the default one | `{}` | +| | +| `haproxy.enabled` | Use HAProxy as TCP proxy for PXC cluster | `true` | +| `haproxy.size` | HAProxy target pod quantity. Can't even if `unsafeFlags.pxcSize` is `true` | `3` | +| `haproxy.image` | HAProxy Container image repository | `percona/haproxy:2.8.5` | +| `haproxy.imagePullPolicy` | The policy used to update images | `` | +| `haproxy.imagePullSecrets` | HAProxy Container pull secret | `[]` | +| `haproxy.configuration` | User defined HAProxy options according to HAProxy configuration file syntax | `` | +| `haproxy.priorityClassName` | HAProxy Pod priority Class defined by user | | +| `haproxy.runtimeClassName` | Name of the Kubernetes Runtime Class for HAProxy Pods | | +| `haproxy.exposePrimary.enabled` | Enable or disable exposing `HAProxy` nodes with dedicated IP addresses | `true` | +| `haproxy.exposePrimary.type` | The Kubernetes Service Type used for exposure | `LoadBalancer` | +| `haproxy.exposePrimary.externalTrafficPolicy` | Specifies whether Service for HAProxy primary should [route external traffic to cluster-wide or to node-local endpoints](https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip) (it can influence the load balancing effectiveness) | `""` | +| `haproxy.exposePrimary.internalTrafficPolicy` | Specifies whether Service for HAProxy primary should [route internal traffic to cluster-wide or to node-local endpoints](https://kubernetes.io/docs/concepts/services-networking/service-traffic-policy/) (it can influence the load balancing effectiveness) | `""` | +| `haproxy.exposePrimary.loadBalancerSourceRanges` | The range of client IP addresses from which the load balancer should be reachable (if not set, there is no limitations) | `[]` | +| `haproxy.exposePrimary.loadBalancerIP` | The static IP-address for the load balancer | `""` | +| `haproxy.exposePrimary.annotations` | The Kubernetes annotations for exposed service | `{}` | +| `haproxy.exposePrimary.labels` | The Kubernetes labels for exposed service | `{}` | +| `haproxy.exposeReplicas.enabled` | Enables or disables `haproxy-replicas` Service. This Service default forwards requests to all Percona XtraDB Cluster instances, and it **should not be used for write requests**! | `true` | +| `haproxy.exposeReplicas.onlyReaders` | Setting it to `true` excludes current MySQL primary instance (writer) from the list of Pods, to which `haproxy-replicas` Service directs connections, leaving only the reader instances. | `false` | +| `haproxy.exposeReplicas.type` | The Kubernetes Service Type used for exposure | `LoadBalancer` | +| `haproxy.exposeReplicas.externalTrafficPolicy` | Specifies whether Service for HAProxy replicas should [route external traffic to cluster-wide or to node-local endpoints](https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip) (it can influence the load balancing effectiveness) | `""` | +| `haproxy.exposeReplicas.internalTrafficPolicy` | Specifies whether Service for HAProxy replicas should [route internal traffic to cluster-wide or to node-local endpoints](https://kubernetes.io/docs/concepts/services-networking/service-traffic-policy/) (it can influence the load balancing effectiveness) | `""` | +| `haproxy.exposeReplicas.loadBalancerSourceRanges` | The range of client IP addresses from which the load balancer should be reachable (if not set, there is no limitations) | `[]` | +| `haproxy.exposeReplicas.loadBalancerIP` | The static IP-address for the load balancer | `""` | +| `haproxy.exposeReplicas.annotations` | The Kubernetes annotations for exposed service | `{}` | +| `haproxy.exposeReplicas.labels` | The Kubernetes labels for exposed service | `{}` | +| `haproxy.annotations` | HAProxy Pod user-defined annotations | `{}` | +| `haproxy.labels` | HAProxy Pod user-defined labels | `{}` | +| `haproxy.schedulerName` | The Kubernetes Scheduler | | +| `haproxy.readinessDelaySec` | HAProxy Pod delay for readiness probe in seconds | `15` | +| `haproxy.livenessDelaySec` | HAProxy Pod delay for liveness probe in seconds | `300` | +| `haproxy.envVarsSecret` | A secret with environment variables | `` | +| `haproxy.resources.requests` | HAProxy Pods resource requests | `{"memory": "1G", "cpu": "600m"}` | +| `haproxy.resources.limits` | HAProxy Pods resource limits | `{}` | +| `haproxy.sidecars` | HAProxy Pods sidecars | `[]` | +| `haproxy.sidecarVolumes` | HAProxy Pods sidecarVolumes | `[]` | +| `haproxy.sidecarPVCs` | HAProxy Pods sidecar PVCs | `[]` | +| `haproxy.sidecarResources.requests` | HAProxy sidecar resource requests | `{}` | +| `haproxy.sidecarResources.limits` | HAProxy sidecar resource limits | `{}` | +| `haproxy.nodeSelector` | HAProxy Pods key-value pairs setting for K8S node assingment | `{}` | +| `haproxy.topologySpreadConstraints` | The Label selector for the [Kubernetes Pod Topology Spread Constraints](https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/) | `[]` | +| `haproxy.affinity.antiAffinityTopologyKey` | HAProxy Pods simple scheduling restriction on/off for host, zone, region | `"kubernetes.io/hostname"` | +| `haproxy.affinity.advanced` | HAProxy Pods advanced scheduling restriction with match expression engine | `{}` | +| `haproxy.tolerations` | List of node taints to tolerate for HAProxy Pods | `[]` | +| `haproxy.gracePeriod` | Allowed time for graceful shutdown | `600` | +| `haproxy.lifecycle.preStop.exec.command` | Command for the [preStop lifecycle hook](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/) for HAProxy Pods | `""` | +| `haproxy.lifecycle.postStart.exec.command` | Command for the [postStart lifecycle hook](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/) for HAProxy Pods | `600` | +| `haproxy.podDisruptionBudget.maxUnavailable` | Instruct Kubernetes about the failed pods allowed quantity | `1` | +| `haproxy.readinessProbes.failureThreshold` | When a probe fails, Kubernetes will try failureThreshold times before giving up | `5` | +| `haproxy.readinessProbes.initialDelaySeconds` | Number of seconds after the container has started before liveness or readiness probes are initiated | `15` | +| `haproxy.readinessProbes.periodSeconds` | How often (in seconds) to perform the probe | `30` | +| `haproxy.readinessProbes.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed | `1` | +| `haproxy.readinessProbes.timeoutSeconds` | Number of seconds after which the probe times out | `15` | +| `haproxy.livenessProbes.failureThreshold` | When a probe fails, Kubernetes will try failureThreshold times before giving up | `3` | +| `haproxy.livenessProbes.initialDelaySeconds` | Number of seconds after the container has started before liveness or readiness probes are initiated | `300` | +| `haproxy.livenessProbes.periodSeconds` | How often (in seconds) to perform the probe | `10` | +| `haproxy.livenessProbes.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed | `1` | +| `haproxy.livenessProbes.timeoutSeconds` | Number of seconds after which the probe times out | `5` | +| `haproxy.containerSecurityContext` | A custom Kubernetes Security Context for a Container to be used instead of the default one | `{}` | +| `haproxy.podSecurityContext` | A custom Kubernetes Security Context for a Pod to be used instead of the default one | `{}` | +| | +| `proxysql.enabled` | Use ProxySQL as TCP proxy for PXC cluster | `false` | +| `proxysql.size` | ProxySQL target pod quantity. Can't even if `unsafeFlags.pxcSize` is `true` | `3` | +| `proxysql.image` | ProxySQL Container image | `percona/proxysql2:2.5.5` | +| `proxysql.imagePullPolicy` | The policy used to update images | `` | +| `proxysql.imagePullSecrets` | ProxySQL Container pull secret | `[]` | +| `proxysql.configuration` | User defined ProxySQL options according to ProxySQL configuration file syntax | `` | +| `proxysql.priorityClassName` | ProxySQL Pod priority Class defined by user | | +| `proxysql.runtimeClassName` | Name of the Kubernetes Runtime Class for ProxySQL Pods | | +| `proxysql.expose.enabled` | Enable or disable exposing `ProxySQL` nodes with dedicated IP addresses | `true` | +| `proxysql.expose.type` | The Kubernetes Service Type used for exposure | `LoadBalancer` | +| `proxysql.expose.externalTrafficPolicy` | Specifies whether Service for ProxySQL nodes should [route external traffic to cluster-wide or to node-local endpoints](https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip) (it can influence the load balancing effectiveness) | `""` | +| `proxysql.expose.internalTrafficPolicy` | Specifies whether Service for ProxySQL nodes should [route internal traffic to cluster-wide or to node-local endpoints](https://kubernetes.io/docs/concepts/services-networking/service-traffic-policy/) (it can influence the load balancing effectiveness) | `""` | +| `proxysql.expose.loadBalancerSourceRanges` | The range of client IP addresses from which the load balancer should be reachable (if not set, there is no limitations) | `[]` | +| `proxysql.expose.loadBalancerIP` | The static IP-address for the load balancer | `""` | +| `proxysql.expose.annotations` | The Kubernetes annotations for exposed service | `{}` | +| `proxysql.expose.labels` | The Kubernetes labels for exposed service | `{}` | +| `proxysql.annotations` | ProxySQL Pod user-defined annotations | `{}` | +| `proxysql.labels` | ProxySQL Pod user-defined labels | `{}` | +| `proxysql.schedulerName` | The Kubernetes Scheduler | | +| `proxysql.readinessDelaySec` | ProxySQL Pod delay for readiness probe in seconds | `15` | +| `proxysql.livenessDelaySec` | ProxySQL Pod delay for liveness probe in seconds | `300` | +| `proxysql.envVarsSecret` | A secret with environment variables | `` | +| `proxysql.resources.requests` | ProxySQL Pods resource requests | `{"memory": "1G", "cpu": "600m"}` | +| `proxysql.resources.limits` | ProxySQL Pods resource limits | `{}` | +| `proxysql.sidecars` | ProxySQL Pods sidecars | `[]` | +| `proxysql.sidecarVolumes` | ProxySQL Pods sidecarVolumes | `[]` | +| `proxysql.sidecarPVCs` | ProxySQL Pods sidecar PVCs | `[]` | +| `proxysql.sidecarResources.requests` | ProxySQL sidecar resource requests | `{}` | +| `proxysql.sidecarResources.limits` | ProxySQL sidecar resource limits | `{}` | +| `proxysql.nodeSelector` | ProxySQL Pods key-value pairs setting for K8S node assingment | `{}` | +| `proxysql.topologySpreadConstraints` | The Label selector for the [Kubernetes Pod Topology Spread Constraints](https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/) | `[]` | +| `proxysql.affinity.antiAffinityTopologyKey` | ProxySQL Pods simple scheduling restriction on/off for host, zone, region | `"kubernetes.io/hostname"` | +| `proxysql.affinity.advanced` | ProxySQL Pods advanced scheduling restriction with match expression engine | `{}` | +| `proxysql.tolerations` | List of node taints to tolerate for ProxySQL Pods | `[]` | +| `proxysql.gracePeriod` | Allowed time for graceful shutdown | `600` | +| `proxysql.lifecycle.preStop.exec.command` | Command for the [preStop lifecycle hook](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/) for ProxySQL Pods | `""` | +| `proxysql.lifecycle.postStart.exec.command` | Command for the [postStart lifecycle hook](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/) for ProxySQL Pods | `600` | +| `proxysql.podDisruptionBudget.maxUnavailable` | Instruct Kubernetes about the failed pods allowed quantity | `1` | +| `proxysql.persistence.enabled` | Requests a persistent storage (`hostPath` or `storageClass`) from K8S for ProxySQL Pods | `true` | +| `proxysql.persistence.hostPath` | Sets datadir path on K8S node for all ProxySQL Pods. Available only when `proxysql.persistence.enabled: true` | | +| `proxysql.persistence.storageClass` | Sets K8S storageClass name for all ProxySQL Pods PVC. Available only when `proxysql.persistence.enabled: true` | `-` | +| `proxysql.persistence.accessMode` | Sets K8S persistent storage access policy for all ProxySQL Pods | `ReadWriteOnce` | +| `proxysql.persistence.size` | Sets K8S persistent storage size for all ProxySQL Pods | `8Gi` | +| `proxysql.containerSecurityContext` | A custom Kubernetes Security Context for a Container to be used instead of the default one | `{}` | +| `proxysql.podSecurityContext` | A custom Kubernetes Security Context for a Pod to be used instead of the default one | `{}` | +| | +| `logcollector.enabled` | Enable log collector container | `true` | +| `logcollector.image` | Log collector image repository | `percona/percona-xtradb-cluster-operator:1.15.1-logcollector-fluentbit3.1.4` | +| `logcollector.imagePullSecrets` | Log collector pull secret | `[]` | +| `logcollector.imagePullPolicy` | The policy used to update images | `` | +| `logcollector.configuration` | User defined configuration for logcollector | `` | +| `logcollector.resources.requests` | Log collector resource requests | `{"memory": "100M", "cpu": "200m"}` | +| `logcollector.resources.limits` | Log collector resource limits | `{}` | +| `logcollector.containerSecurityContext` | A custom Kubernetes Security Context for a Container to be used instead of the default one | `{}` | +| | +| `pmm.enabled` | Enable integration with [Percona Monitoring and Management software](https://www.percona.com/doc/kubernetes-operator-for-pxc/monitoring.html) | `false` | +| `pmm.image.repository` | PMM Container image repository | `percona/pmm-client` | +| `pmm.image.tag` | PMM Container image tag | `2.42.0` | +| `pmm.imagePullSecrets` | PMM Container pull secret | `[]` | +| `pmm.imagePullPolicy` | The policy used to update images | `` | +| `pmm.serverHost` | PMM server related K8S service hostname | `monitoring-service` | +| `pmm.serverUser` | Username for accessing PXC database internals | `admin` | +| `pmm.resources.requests` | PMM Container resource requests | `{"memory": "150M", "cpu": "300m"}` | +| `pmm.resources.limits` | PMM Container resource limits | `{}` | +| `pmm.pxcParams` | Additional parameters which will be passed to the [pmm-admin add mysql](https://docs.percona.com/percona-monitoring-and-management/setting-up/client/mysql.html#add-service) command for `pxc` Pods | `""` | +| `pmm.proxysqlParams` | Additional parameters which will be passed to the [pmm-admin add proxysql](https://docs.percona.com/percona-monitoring-and-management/setting-up/client/proxysql.html) command for `proxysql` Pods | `""` | +| `pmm.containerSecurityContext` | A custom Kubernetes Security Context for a Container to be used instead of the default one | `{}` | +| | +| `backup.enabled` | Enables backups for PXC cluster | `true` | +| `backup.allowParallel` | Allow taking multiple backups in parallel | `true` | +| `backup.image.repository` | Backup Container image | `percona/percona-xtradb-cluster-operator` | +| `backup.image.tag` | Backup Container tag | `1.15.1-pxc8.0-backup-pxb8.0.35` | +| `backup.backoffLimit` | The number of retries to make a backup | `10` | +| `backup.imagePullSecrets` | Backup Container pull secret | `[]` | +| `backup.imagePullPolicy` | The policy used to update images | `` | +| `backup.pitr.enabled` | Enable point in time recovery | `false` | +| `backup.pitr.storageName` | Storage name for PITR | `s3-us-west-binlogs` | +| `backup.pitr.timeBetweenUploads` | Time between uploads for PITR | `60` | +| `backup.pitr.timeoutSeconds` | Timeout in seconds for the binlog to be uploaded; the binlog uploader container will be restarted after exceeding this timeout | `60` | +| `backup.pitr.resources.requests` | PITR Container resource requests | `{}` | +| `backup.pitr.resources.limits` | PITR Container resource limits | `{}` | +| `backup.storages.fs-pvc` | Backups storage configuration, where `storages:` is a high-level key for the underlying structure. `fs-pvc` is a user-defined storage name. | | +| `backup.storages.fs-pvc.type` | Backup storage type | `filysystem` | +| `backup.storages.fs-pvc.verifyTLS` | Enable or disable verification of the storage server TLS certificate | `true` | +| `backup.storages.fs-pvc.volume.persistentVolumeClaim.accessModes` | Backup PVC access policy | `["ReadWriteOnce"]` | +| `backup.storages.fs-pvc.volume.persistentVolumeClaim.resources` | Backup Pod resources specification | `{}` | +| `backup.storages.fs-pvc.volume.persistentVolumeClaim.resources.requests.storage` | Backup Pod datadir backups size | `6Gi` | +| `backup.storages.fs-pvc.topologySpreadConstraints` | The Label selector for the [Kubernetes Pod Topology Spread Constraints](https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/) | `[]` | +| `backup.storages.fs-pvc.containerOptions.env` | Environment variables to add to the backup container | `[]` | +| `backup.storages.fs-pvc.containerOptions.args.xtrabackup` | Additional arguments for xtrabackup | `[]` | +| `backup.storages.fs-pvc.containerOptions.args.xbstream` | Additional arguments for xbstream | `[]` | +| `backup.storages.fs-pvc.containerOptions.args.xbcloud` | Additional arguments for xbcloud | `[]` | +| `backup.schedule` | Backup execution timetable | `[]` | +| `backup.schedule.0.name` | Backup execution timetable name | `daily-backup` | +| `backup.schedule.0.schedule` | Backup execution timetable cron timing | `0 0 * * *` | +| `backup.schedule.0.keep` | Backup items to keep | `5` | +| `backup.schedule.0.storageName` | Backup target storage | `fs-pvc` | +| | +| `secrets.passwords.root` | Default user secret | `insecure-root-password` | +| `secrets.passwords.xtrabackup` | Default user secret | `insecure-xtrabackup-password` | +| `secrets.passwords.monitor` | Default user secret | `insecure-monitor-password` | +| `secrets.passwords.clustercheck` | Default user secret | `insecure-clustercheck-password` | +| `secrets.passwords.proxyadmin` | Default user secret | `insecure-proxyadmin-password` | +| `secrets.passwords.pmmserver` | Default user secret | `insecure-pmmserver-password` | +| `secrets.passwords.pmmserverkey` | PMM server API key | `` | +| `secrets.passwords.operator` | Default user secret | `insecure-operator-password` | +| `secrets.passwords.replication` | Default user secret | `insecure-replication-password` | +| `secrets.tls.cluster` | Specify secret name for TLS. Not needed in case if you're using cert-manager. Structure expects keys `ca.crt`, `tls.crt`, `tls.key` and files contents encoded in base64. | `` | +| `secrets.tls.internal` | Specify internal secret name for TLS. | `` | +| `secrets.logCollector` | Specify secret name used for Fluent Bit Log Collector | `` | +| `secrets.vault` | Specify secret name used for HashiCorp Vault to carry on Data at Rest Encryption | `` | + + +Specify parameters using `--set key=value[,key=value]` argument to `helm install` + +## Examples + +### Deploy a Cluster without a MySQL Proxy, no backups, no persistent disks + +This is great for a dev cluster as it doesn't require a persistent disk and doesn't bother with a proxy, backups, or TLS. + +```bash +$ helm install dev --namespace pxc . \ + --set proxysql.enabled=false --set tls.enabled=false --set unsafeFlags.tls=true \ + --set pxc.persistence.enabled=false --set backup-enabled=false +``` + +### Deploy a cluster with certificates provided by Cert Manager + +First you need a working cert-manager installed with appropriate Issuers set up. Check out the [JetStack Helm Chart](https://hub.helm.sh/charts/jetstack/cert-manager) to do that. + +By setting `pxc.certManager=true` we're signaling the Helm chart to not create secrets,which will in turn let the operator know to request appropriate `certificate` resources to be filled by cert-manager. + +```bash +$ helm install dev --namespace pxc . --set pxc.certManager=true +``` + +### Deploy a production grade cluster + +The pxc-database chart contains an example production values file that should set you +well on your path to running a production database. It is not fully production grade as +there are some requirements for you to provide your own secrets for passwords and TLS to be truly production ready, but it does provide comments on how to do those parts. + +```bash +$ helm install prod --file production-values.yaml --namespace pxc . +``` diff --git a/charts/percona/pxc-db/1.15.1/templates/NOTES.txt b/charts/percona/pxc-db/1.15.1/templates/NOTES.txt new file mode 100644 index 000000000..58d5564d4 --- /dev/null +++ b/charts/percona/pxc-db/1.15.1/templates/NOTES.txt @@ -0,0 +1,56 @@ +# + + % _____ + %%% | __ \ + ###%%%%%%%%%%%%* | |__) |__ _ __ ___ ___ _ __ __ _ + ### ##%% %%%% | ___/ _ \ '__/ __/ _ \| '_ \ / _` | + #### ##% %%%% | | | __/ | | (_| (_) | | | | (_| | + ### #### %%% |_| \___|_| \___\___/|_| |_|\__,_| + ,((### ### %%% _ _ _____ _ + (((( (### #### %%%% | | / _ \ / ____| | | + ((( ((# ###### | | _| (_) |___ | (___ __ _ _ _ __ _ __| | + (((( (((# #### | |/ /> _ >> https://percona.com/k8s <<< + +1. To get a MySQL prompt inside your new cluster you can run: + + {{- if hasKey .Values.pxc "clusterSecretName" }} + ROOT_PASSWORD=`kubectl -n {{ .Release.Namespace }} get secrets {{ .Values.pxc.clusterSecretName }} -o jsonpath="{.data.root}" | base64 --decode` + kubectl -n {{ .Release.Namespace }} exec -ti \ + {{ include "pxc-database.fullname" . }}-pxc-0 -c pxc -- mysql -uroot -p"$ROOT_PASSWORD" + {{- else }} + ROOT_PASSWORD=`kubectl -n {{ .Release.Namespace }} get secrets {{ include "pxc-database.fullname" . }}-secrets -o jsonpath="{.data.root}" | base64 --decode` + kubectl -n {{ .Release.Namespace }} exec -ti \ + {{ include "pxc-database.fullname" . }}-pxc-0 -c pxc -- mysql -uroot -p"$ROOT_PASSWORD" + {{- end }} + + +2. To connect an Application running in the same Kubernetes cluster you can connect with: + + {{- if hasKey .Values.pxc "clusterSecretName" }} + ROOT_PASSWORD=`kubectl -n {{ .Release.Namespace }} get secrets {{ .Values.pxc.clusterSecretName }} -o jsonpath="{.data.root}" | base64 --decode` + {{- else }} + ROOT_PASSWORD=`kubectl -n {{ .Release.Namespace }} get secrets {{ include "pxc-database.fullname" . }}-secrets -o jsonpath="{.data.root}" | base64 --decode` + {{- end }} + + +{{- if .Values.proxysql.enabled }} + + kubectl run -i --tty --rm percona-client --image=percona --restart=Never \ + -- mysql -h {{ template "pxc-database.fullname" . }}-proxysql.{{ .Release.Namespace }}.svc.cluster.local -uroot -p"$ROOT_PASSWORD" + +{{- else }} + + kubectl run -i --tty --rm percona-client --image=percona --restart=Never \ + -- mysql -h {{ template "pxc-database.fullname" . }}-haproxy.{{ .Release.Namespace }}.svc.cluster.local -uroot -p"$ROOT_PASSWORD" + +{{- end }} + diff --git a/charts/percona/pxc-db/1.15.1/templates/_helpers.tpl b/charts/percona/pxc-db/1.15.1/templates/_helpers.tpl new file mode 100644 index 000000000..0b0611ad1 --- /dev/null +++ b/charts/percona/pxc-db/1.15.1/templates/_helpers.tpl @@ -0,0 +1,77 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "pxc-database.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 "pxc-database.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 21 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 21 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 21 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "pxc-database.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 21 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Common labels +*/}} +{{- define "pxc-database.labels" -}} +app.kubernetes.io/name: {{ include "pxc-database.name" . }} +helm.sh/chart: {{ include "pxc-database.chart" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} + +{{/* +This filters the backup.storages hash for S3 credentials. If we detect them, they go in a separate secret. +*/}} +{{- define "pxc-database.storages" -}} +{{- $storages := dict -}} +{{- range $key, $value := .Values.backup.storages -}} +{{- if and (hasKey $value "type") (eq $value.type "s3") (hasKey $value "s3") (hasKey (index $value "s3") "credentialsAccessKey") (hasKey (index $value "s3") "credentialsSecretKey") }} +{{- if hasKey (index $value "s3") "credentialsSecret" -}} +{{- fail "credentialsSecret and credentialsAccessKey/credentialsSecretKey isn't supported!" -}} +{{- end -}} +{{- $secretName := printf "%s-s3-%s" (include "pxc-database.fullname" $) $key -}} +{{- $s3 := set (omit (index $value "s3") "credentialsAccessKey" "credentialsSecretKey") "credentialsSecret" $secretName -}} +{{- $_value := set (omit $value "s3") "s3" $s3 -}} +{{- $_ := set $storages $key $_value -}} +{{- else -}} +{{- $_ := set $storages $key $value -}} +{{- end -}} +{{- end -}} +{{- $storages | toYaml -}} +{{- end -}} + +{{/* +Functions returns image URI according to parameters set +*/}} +{{- define "pxc-db.operator-image" -}} +{{- if .Values.image }} +{{- .Values.image }} +{{- else }} +{{- printf "%s:%s" .Values.operatorImageRepository .Chart.AppVersion }} +{{- end }} +{{- end -}} diff --git a/charts/percona/pxc-db/1.15.1/templates/cluster-secret.yaml b/charts/percona/pxc-db/1.15.1/templates/cluster-secret.yaml new file mode 100644 index 000000000..da07f331a --- /dev/null +++ b/charts/percona/pxc-db/1.15.1/templates/cluster-secret.yaml @@ -0,0 +1,27 @@ +{{- if hasKey .Values.secrets "passwords" }} +apiVersion: v1 +kind: Secret +metadata: + {{- if hasKey .Values.pxc "clusterSecretName" }} + name: {{ .Values.pxc.clusterSecretName }} + {{- else }} + name: {{ include "pxc-database.fullname" . }}-secrets + {{- end }} + namespace: {{ .Release.Namespace }} + labels: +{{ include "pxc-database.labels" . | indent 4 }} +type: Opaque +data: + root: {{ .Values.secrets.passwords.root | b64enc }} + xtrabackup: {{ .Values.secrets.passwords.xtrabackup | b64enc }} + monitor: {{ .Values.secrets.passwords.monitor | b64enc }} + clustercheck: {{ .Values.secrets.passwords.clustercheck | b64enc }} + proxyadmin: {{ .Values.secrets.passwords.proxyadmin | b64enc }} + {{- if hasKey .Values.secrets.passwords "pmmserverkey" }} + pmmserverkey: {{ .Values.secrets.passwords.pmmserverkey | b64enc }} + {{- else if hasKey .Values.secrets.passwords "pmmserver" }} + pmmserver: {{ .Values.secrets.passwords.pmmserver | b64enc }} + {{- end}} + operator: {{ .Values.secrets.passwords.operator | b64enc }} + replication: {{ .Values.secrets.passwords.replication | b64enc }} +{{- end }} diff --git a/charts/percona/pxc-db/1.15.1/templates/cluster-ssl-secret.yaml b/charts/percona/pxc-db/1.15.1/templates/cluster-ssl-secret.yaml new file mode 100644 index 000000000..0d217275f --- /dev/null +++ b/charts/percona/pxc-db/1.15.1/templates/cluster-ssl-secret.yaml @@ -0,0 +1,42 @@ +{{- if .Values.tls.enabled }} +{{- if not .Values.pxc.certManager }} +{{- $nameDB := printf "%s" (include "pxc-database.fullname" .) }} +{{ $ca := genCA (printf "%s-ca" $nameDB ) 365 }} +{{- if not (hasKey .Values.secrets.tls "cluster") }} +--- +{{- $name := printf "%s-proxysql" $nameDB }} +{{- $altNames := list ( printf "%s-pxc" $nameDB ) ( printf "*.%s-pxc" $nameDB ) ( printf "*.%s-proxysql" $nameDB ) -}} +{{ $cert := genSignedCert $name nil $altNames 365 $ca }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ $nameDB }}-ssl + namespace: {{ .Release.Namespace }} + labels: +{{ include "pxc-database.labels" . | indent 4 }} +type: kubernetes.io/tls +data: + ca.crt: {{ $ca.Cert | b64enc }} + tls.crt: {{ $cert.Cert | b64enc }} + tls.key: {{ $cert.Key | b64enc }} +{{- end }} +{{- if not (hasKey .Values.secrets.tls "internal") }} +--- +{{- $name := printf "%s-pxc" $nameDB }} +{{- $altNames := list ( printf "%s" $name ) ( printf "*.%s" $name ) ( printf "%s-haproxy-replicas.%s.svc.cluster.local" $nameDB .Release.Namespace ) ( printf "%s-haproxy-replicas.%s" $nameDB .Release.Namespace ) ( printf "%s-haproxy-replicas" $nameDB ) ( printf "%s-haproxy.%s.svc.cluster.local" $nameDB .Release.Namespace ) ( printf "%s-haproxy.%s" $nameDB .Release.Namespace ) ( printf "%s-haproxy" $nameDB ) -}} +{{ $cert := genSignedCert $name nil $altNames 365 $ca }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ $nameDB }}-ssl-internal + namespace: {{ .Release.Namespace }} + labels: +{{ include "pxc-database.labels" . | indent 4 }} +type: kubernetes.io/tls +data: + ca.crt: {{ $ca.Cert | b64enc }} + tls.crt: {{ $cert.Cert | b64enc }} + tls.key: {{ $cert.Key | b64enc }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/percona/pxc-db/1.15.1/templates/cluster.yaml b/charts/percona/pxc-db/1.15.1/templates/cluster.yaml new file mode 100644 index 000000000..e10adefb5 --- /dev/null +++ b/charts/percona/pxc-db/1.15.1/templates/cluster.yaml @@ -0,0 +1,544 @@ +apiVersion: pxc.percona.com/v1 +kind: PerconaXtraDBCluster +metadata: + name: {{ include "pxc-database.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: +{{ include "pxc-database.labels" . | indent 4 }} + finalizers: +{{ .Values.finalizers | toYaml | indent 4 }} + {{- with .Values.annotations }} + annotations: + {{- . | toYaml | nindent 4 }} + {{- end }} +spec: + crVersion: {{ .Chart.AppVersion }} + enableVolumeExpansion: {{ .Values.enableVolumeExpansion }} + {{- if .Values.ignoreAnnotations }} + ignoreAnnotations: +{{ .Values.ignoreAnnotations | toYaml | indent 4 }} + {{- end }} + {{- if .Values.ignoreLabels }} + ignoreLabels: +{{ .Values.ignoreLabels | toYaml | indent 4 }} + {{- end }} + {{- if hasKey .Values.pxc "clusterSecretName" }} + secretsName: {{ .Values.pxc.clusterSecretName }} + {{- else }} + secretsName: {{ include "pxc-database.fullname" . }}-secrets + {{- end }} + {{- if .Values.tls.enabled }} + {{- if hasKey .Values.secrets.tls "cluster" }} + sslSecretName: {{ .Values.secrets.tls.cluster }} + {{- else }} + sslSecretName: {{ include "pxc-database.fullname" . }}-ssl + {{- end }} + {{- if hasKey .Values.secrets.tls "internal" }} + sslInternalSecretName: {{ .Values.secrets.tls.internal }} + {{- else }} + sslInternalSecretName: {{ include "pxc-database.fullname" . }}-ssl-internal + {{- end }} + {{- end }} + {{- if hasKey .Values.secrets "vault" }} + vaultSecretName: {{ .Values.secrets.vault }} + {{- else }} + vaultSecretName: {{ include "pxc-database.fullname" . }}-vault + {{- end }} + {{- if hasKey .Values.secrets "logCollector" }} + logCollectorSecretName: {{ .Values.secrets.logCollector }} + {{- else }} + logCollectorSecretName: {{ include "pxc-database.fullname" . }}-log-collector + {{- end }} + {{- if .Values.initContainer }} + initContainer: + {{- if hasKey .Values.initContainer "image" }} + image: {{ .Values.initContainer.image }} + {{- else }} + image: {{ include "pxc-db.operator-image" . }} + {{- end }} + {{- if .Values.initContainer.resources }} + resources: + {{- if hasKey .Values.initContainer.resources "requests" }} + requests: +{{ tpl (.Values.initContainer.resources.requests | toYaml) $ | indent 8 }} + {{- end }} + {{- if hasKey .Values.initContainer.resources "limits" }} + limits: +{{ tpl (.Values.initContainer.resources.limits | toYaml) $ | indent 8 }} + {{- end }} + {{- end }} + {{- end }} + enableCRValidationWebhook: {{ .Values.enableCRValidationWebhook }} + pause: {{ .Values.pause }} + {{- if .Values.unsafeFlags }} + unsafeFlags: +{{ .Values.unsafeFlags | toYaml | indent 4 }} + {{- end }} + updateStrategy: {{ .Values.updateStrategy }} + {{- if hasKey .Values.upgradeOptions "versionServiceEndpoint" }} + upgradeOptions: + versionServiceEndpoint: {{ .Values.upgradeOptions.versionServiceEndpoint }} + apply: {{ .Values.upgradeOptions.apply }} + schedule: {{ .Values.upgradeOptions.schedule }} + {{- end }} + {{- if .Values.tls }} + tls: + enabled: {{ .Values.tls.enabled }} + {{- if hasKey .Values.tls "SANs" }} + SANs: +{{ .Values.tls.SANs | toYaml | indent 6 }} + {{- end }} + {{- if hasKey .Values.tls "issuerConf" }} + issuerConf: + name: {{ .Values.tls.issuerConf.name }} + kind: {{ .Values.tls.issuerConf.kind }} + group: {{ .Values.tls.issuerConf.group }} + {{- end }} + {{- end }} + + {{- $pxc := .Values.pxc }} + pxc: + size: {{ $pxc.size }} + image: {{ $pxc.image.repository }}:{{ $pxc.image.tag }} + autoRecovery: {{ $pxc.autoRecovery }} + {{- if $pxc.schedulerName }} + schedulerName: {{ $pxc.schedulerName }} + {{- end }} + readinessDelaySec: {{ $pxc.readinessDelaySec }} + livenessDelaySec: {{ $pxc.livenessDelaySec }} + {{- if $pxc.configuration }} + configuration: | + {{ tpl $pxc.configuration $ | nindent 6 }} + {{- end }} + {{- if $pxc.imagePullPolicy }} + imagePullPolicy: {{ $pxc.imagePullPolicy }} + {{- end }} + {{- if $pxc.imagePullSecrets }} + imagePullSecrets: +{{ $pxc.imagePullSecrets | toYaml | indent 6 }} + {{- end }} + {{- if $pxc.priorityClassName }} + priorityClassName: {{ $pxc.priorityClassName }} + {{- end }} + annotations: +{{ $pxc.annotations | toYaml | indent 6 }} + labels: +{{ $pxc.labels | toYaml | indent 6 }} + {{- if $pxc.expose }} + expose: +{{ tpl ($pxc.expose | toYaml) $ | indent 6 }} + {{- end }} + {{- if $pxc.replicationChannels }} + replicationChannels: +{{ tpl ($pxc.replicationChannels | toYaml) $ | indent 6 }} + {{- end }} + {{- if $pxc.runtimeClassName }} + runtimeClassName: {{ $pxc.runtimeClassName }} + {{- end }} + {{- if $pxc.envVarsSecret }} + envVarsSecret: {{ $pxc.envVarsSecret }} + {{- end }} + resources: + requests: +{{ tpl ($pxc.resources.requests | toYaml) $ | indent 8 }} + limits: +{{ tpl ($pxc.resources.limits | toYaml) $ | indent 8 }} + sidecars: +{{ $pxc.sidecars | toYaml | indent 6 }} + sidecarVolumes: +{{ $pxc.sidecarVolumes | toYaml | indent 6 }} + sidecarPVCs: +{{ $pxc.sidecarPVCs | toYaml | indent 6 }} + sidecarResources: + requests: +{{ tpl ($pxc.sidecarResources.requests | toYaml) $ | indent 8 }} + limits: +{{ tpl ($pxc.sidecarResources.limits | toYaml) $ | indent 8 }} + nodeSelector: +{{ $pxc.nodeSelector | toYaml | indent 6 }} + {{- if $pxc.topologySpreadConstraints }} + topologySpreadConstraints: +{{ $pxc.topologySpreadConstraints | toYaml | indent 6 }} + {{- end }} + affinity: +{{ $pxc.affinity | toYaml | indent 6 }} + tolerations: +{{ $pxc.tolerations | toYaml | indent 6 }} + podDisruptionBudget: +{{ $pxc.podDisruptionBudget | toYaml | indent 6 }} + volumeSpec: + {{- if not $pxc.persistence.enabled }} + emptyDir: {} + {{- else }} + {{- if hasKey $pxc.persistence "hostPath" }} + hostPath: + path: {{ $pxc.persistence.hostPath }} + type: Directory + {{- else }} + persistentVolumeClaim: + {{- if $pxc.persistence.storageClass }} + {{- if (eq "-" $pxc.persistence.storageClass) }} + storageClassName: "" + {{- else }} + storageClassName: "{{ $pxc.persistence.storageClass }}" + {{- end }} + {{- end }} + accessModes: [{{ $pxc.persistence.accessMode | quote }}] + {{- if $pxc.persistence.dataSource }} + dataSource: +{{ $pxc.persistence.dataSource| toYaml | indent 10 }} + {{- end }} + resources: + requests: + storage: {{ $pxc.persistence.size | quote }} + {{- end }} + {{- end }} + gracePeriod: {{ $pxc.gracePeriod }} + {{- if hasKey $pxc "lifecycle" }} + lifecycle: + {{- if hasKey $pxc.lifecycle "preStop" }} + preStop: + {{- $pxc.lifecycle.preStop | toYaml | nindent 8 }} + {{- end }} + {{- if hasKey $pxc.lifecycle "postStart" }} + postStart: + {{- $pxc.lifecycle.postStart | toYaml | nindent 8 }} + {{- end }} + {{- end }} + readinessProbes: +{{ tpl ($pxc.readinessProbes | toYaml) $ | indent 6 }} + livenessProbes: +{{ tpl ($pxc.livenessProbes | toYaml) $ | indent 6 }} + {{- if $pxc.containerSecurityContext }} + containerSecurityContext: +{{ tpl ($pxc.containerSecurityContext | toYaml) $ | indent 6 }} + {{- end }} + {{- if $pxc.podSecurityContext }} + podSecurityContext: +{{ tpl ($pxc.podSecurityContext | toYaml) $ | indent 6 }} + {{- end }} + {{- if $pxc.serviceAccountName }} + serviceAccountName: {{ $pxc.serviceAccountName }} + {{- end }} + {{- if or (not .Values.haproxy.enabled) .Values.proxysql.enabled }} + haproxy: + enabled: false + {{- else }} + {{- $haproxy := .Values.haproxy }} + haproxy: + enabled: true + size: {{ $haproxy.size }} + image: {{ .Values.haproxy.image }} + {{- if $haproxy.imagePullPolicy }} + imagePullPolicy: {{ $haproxy.imagePullPolicy }} + {{- end }} + {{- if $haproxy.imagePullSecrets }} + imagePullSecrets: +{{ $haproxy.imagePullSecrets | toYaml | indent 6 }} + {{- end }} + {{- if $haproxy.schedulerName }} + schedulerName: {{ $haproxy.schedulerName }} + {{- end }} + {{- if $haproxy.configuration }} + configuration: | + {{ tpl $haproxy.configuration $ | nindent 6 }} + {{- end }} + {{- if $haproxy.priorityClassName }} + priorityClassName: {{ $haproxy.priorityClassName }} + {{- end }} + {{- if $haproxy.exposePrimary }} + exposePrimary: +{{ tpl ($haproxy.exposePrimary | toYaml) $ | indent 6 }} + {{- end }} + {{- if $haproxy.exposeReplicas }} + exposeReplicas: +{{ tpl ($haproxy.exposeReplicas | toYaml) $ | indent 6 }} + {{- end }} + annotations: +{{ $haproxy.annotations | toYaml | indent 6 }} + labels: +{{ $haproxy.labels | toYaml | indent 6 }} + {{- if $haproxy.runtimeClassName }} + runtimeClassName: {{ $haproxy.runtimeClassName }} + {{- end }} + {{- if $haproxy.envVarsSecret }} + envVarsSecret: {{ $haproxy.envVarsSecret }} + {{- end }} + resources: + requests: +{{ $haproxy.resources.requests | toYaml | indent 8 }} + limits: +{{ $haproxy.resources.limits | toYaml | indent 8 }} + sidecars: +{{ $haproxy.sidecars | toYaml | indent 6 }} + sidecarVolumes: +{{ $haproxy.sidecarVolumes | toYaml | indent 6 }} + sidecarPVCs: +{{ $haproxy.sidecarPVCs | toYaml | indent 6 }} + sidecarResources: + requests: +{{ tpl ($haproxy.sidecarResources.requests | toYaml) $ | indent 8 }} + limits: +{{ tpl ($haproxy.sidecarResources.limits | toYaml) $ | indent 8 }} + {{- if $haproxy.serviceAccountName }} + serviceAccountName: {{ $haproxy.serviceAccountName }} + {{- end }} + nodeSelector: +{{ $haproxy.nodeSelector | toYaml | indent 6 }} + {{- if $haproxy.topologySpreadConstraints }} + topologySpreadConstraints: +{{ $haproxy.topologySpreadConstraints | toYaml | indent 6 }} + {{- end }} + affinity: +{{ $haproxy.affinity | toYaml | indent 6 }} + tolerations: +{{ $haproxy.tolerations | toYaml | indent 6 }} + podDisruptionBudget: +{{ $haproxy.podDisruptionBudget | toYaml | indent 6 }} + volumeSpec: + emptyDir: {} + gracePeriod: {{ $haproxy.gracePeriod }} + {{- if hasKey $haproxy "lifecycle" }} + lifecycle: + {{- if hasKey $haproxy.lifecycle "preStop" }} + preStop: + {{- $haproxy.lifecycle.preStop | toYaml | nindent 8 }} + {{- end }} + {{- if hasKey $haproxy.lifecycle "postStart" }} + postStart: + {{- $haproxy.lifecycle.postStart | toYaml | nindent 8 }} + {{- end }} + {{- end }} + {{- if $haproxy.readinessDelaySec }} + readinessDelaySec: {{ $haproxy.readinessDelaySec }} + {{- end }} + {{- if $haproxy.livenessDelaySec }} + livenessDelaySec: {{ $pxc.livenessDelaySec }} + {{- end }} + readinessProbes: +{{ tpl ($haproxy.readinessProbes | toYaml) $ | indent 6 }} + livenessProbes: +{{ tpl ($haproxy.livenessProbes | toYaml) $ | indent 6 }} + {{- if $haproxy.containerSecurityContext }} + containerSecurityContext: +{{ tpl ($haproxy.containerSecurityContext | toYaml) $ | indent 6 }} + {{- end }} + {{- if $haproxy.podSecurityContext }} + podSecurityContext: +{{ tpl ($haproxy.podSecurityContext | toYaml) $ | indent 6 }} + {{- end }} + {{- end }} + {{- if not .Values.proxysql.enabled }} + proxysql: + enabled: false + {{- else }} + {{- $proxysql := .Values.proxysql }} + proxysql: + enabled: true + size: {{ $proxysql.size }} + image: {{ .Values.proxysql.image }} + {{- if $proxysql.imagePullPolicy }} + imagePullPolicy: {{ $proxysql.imagePullPolicy }} + {{- end }} + {{- if $proxysql.imagePullSecrets }} + imagePullSecrets: + {{- $proxysql.imagePullSecrets | toYaml | indent 6 }} + {{- end }} + {{- if $proxysql.schedulerName }} + schedulerName: {{ $proxysql.schedulerName }} + {{- end }} + {{- if $proxysql.configuration }} + configuration: | + {{ tpl $proxysql.configuration $ | nindent 6 }} + {{- end }} + {{- if $proxysql.priorityClassName }} + priorityClassName: {{ $proxysql.priorityClassName }} + {{- end }} + {{- if $proxysql.expose }} + expose: +{{ tpl ($proxysql.expose | toYaml) $ | indent 6 }} + {{- end }} + annotations: +{{ $proxysql.annotations | toYaml | indent 6 }} + labels: +{{ $proxysql.labels | toYaml | indent 6 }} + {{- if $proxysql.runtimeClassName }} + runtimeClassName: {{ $proxysql.runtimeClassName }} + {{- end }} + {{- if $proxysql.envVarsSecret }} + envVarsSecret: {{ $proxysql.envVarsSecret }} + {{- end }} + resources: + requests: +{{ $proxysql.resources.requests | toYaml | indent 8 }} + limits: +{{ $proxysql.resources.limits | toYaml | indent 8 }} + sidecars: +{{ $proxysql.sidecars | toYaml | indent 6 }} + sidecarVolumes: +{{ $proxysql.sidecarVolumes | toYaml | indent 6 }} + sidecarPVCs: +{{ $proxysql.sidecarPVCs | toYaml | indent 6 }} + sidecarResources: + requests: +{{ tpl ($proxysql.sidecarResources.requests | toYaml) $ | indent 8 }} + limits: +{{ tpl ($proxysql.sidecarResources.limits | toYaml) $ | indent 8 }} + {{- if $proxysql.serviceAccountName }} + serviceAccountName: {{ $proxysql.serviceAccountName }} + {{- end }} + nodeSelector: +{{ $proxysql.nodeSelector | toYaml | indent 6 }} + {{- if $proxysql.topologySpreadConstraints }} + topologySpreadConstraints: +{{ $proxysql.topologySpreadConstraints | toYaml | indent 6 }} + {{- end }} + affinity: +{{ $proxysql.affinity | toYaml | indent 6 }} + tolerations: +{{ $proxysql.tolerations | toYaml | indent 6 }} + podDisruptionBudget: +{{ $proxysql.podDisruptionBudget | toYaml | indent 6 }} + volumeSpec: + {{- if not $proxysql.persistence.enabled }} + emptyDir: {} + {{- else }} + {{- if hasKey $proxysql.persistence "hostPath" }} + hostPath: + path: {{ $proxysql.persistence.hostPath }} + type: Directory + {{- else }} + persistentVolumeClaim: + {{- if $proxysql.persistence.storageClass }} + {{- if (eq "-" $proxysql.persistence.storageClass) }} + storageClassName: "" + {{- else }} + storageClassName: "{{ $proxysql.persistence.storageClass }}" + {{- end }} + {{- end }} + accessModes: [{{ $proxysql.persistence.accessMode | quote }}] + resources: + requests: + storage: {{ $proxysql.persistence.size | quote }} + {{- end }} + {{- end }} + gracePeriod: {{ $proxysql.gracePeriod }} + {{- if hasKey $proxysql "lifecycle" }} + lifecycle: + {{- if hasKey $proxysql.lifecycle "preStop" }} + preStop: + {{- $proxysql.lifecycle.preStop | toYaml | nindent 8 }} + {{- end }} + {{- if hasKey $proxysql.lifecycle "postStart" }} + postStart: + {{- $proxysql.lifecycle.postStart | toYaml | nindent 8 }} + {{- end }} + {{- end }} + {{- if $proxysql.containerSecurityContext }} + containerSecurityContext: +{{ tpl ($proxysql.containerSecurityContext | toYaml) $ | indent 6 }} + {{- end }} + {{- if $proxysql.podSecurityContext }} + podSecurityContext: +{{ tpl ($proxysql.podSecurityContext | toYaml) $ | indent 6 }} + {{- end }} + {{- end }} + logcollector: + {{- if not .Values.logcollector.enabled }} + enabled: false + {{- else }} + {{- $logcollector := .Values.logcollector }} + enabled: true + image: {{ .Values.logcollector.image }} + {{- if $logcollector.imagePullPolicy }} + imagePullPolicy: {{ $logcollector.imagePullPolicy }} + {{- end }} + {{- if $logcollector.imagePullSecrets }} + imagePullSecrets: + {{- $logcollector.imagePullSecrets | toYaml | nindent 6 }} + {{- end }} + {{- if $logcollector.configuration }} + configuration: | + {{ tpl $logcollector.configuration $ | nindent 6 }} + {{- end }} + resources: + requests: +{{ tpl ($logcollector.resources.requests | toYaml) $ | indent 8 }} + limits: +{{ tpl ($logcollector.resources.limits | toYaml) $ | indent 8 }} + {{- if $logcollector.containerSecurityContext }} + containerSecurityContext: +{{ tpl ($logcollector.containerSecurityContext | toYaml) $ | indent 6 }} + {{- end }} + {{- end }} + pmm: + {{- if not .Values.pmm.enabled }} + enabled: false + {{- else }} + {{- $pmm := .Values.pmm }} + enabled: true + image: {{ $pmm.image.repository }}:{{ $pmm.image.tag }} + {{- if $pmm.imagePullPolicy }} + imagePullPolicy: {{ $pmm.imagePullPolicy }} + {{- end }} + {{- if $pmm.containerSecurityContext }} + containerSecurityContext: +{{ tpl ($pmm.containerSecurityContext | toYaml) $ | indent 6 }} + {{- end }} + {{- if $pmm.imagePullSecrets }} + imagePullSecrets: + {{- $pmm.imagePullSecrets | toYaml | nindent 6 }} + {{- end }} + serverHost: {{ $pmm.serverHost }} + serverUser: {{ $pmm.serverUser }} + {{- if $pmm.pxcParams }} + pxcParams: {{ $pmm.pxcParams }} + {{- end }} + {{- if $pmm.proxysqlParams }} + proxysqlParams: {{ $pmm.proxysqlParams }} + {{- end }} + resources: + requests: +{{ tpl ($pmm.resources.requests | toYaml) $ | indent 8 }} + limits: +{{ tpl ($pmm.resources.limits | toYaml) $ | indent 8 }} + {{- end }} + {{- $backup := .Values.backup }} + {{- if $backup.enabled }} + backup: + {{- if $backup.allowParallel }} + allowParallel: {{ $backup.allowParallel }} + {{- end }} + image: {{ $backup.image.repository }}:{{ $backup.image.tag }} + {{- if $backup.backoffLimit }} + backoffLimit: {{ $backup.backoffLimit }} + {{- end }} + {{- if $backup.serviceAccountName }} + serviceAccountName: {{ $backup.serviceAccountName }} + {{- end }} + {{- if $backup.imagePullPolicy }} + imagePullPolicy: {{ $backup.imagePullPolicy }} + {{- end }} + {{- if $backup.imagePullSecrets }} + imagePullSecrets: +{{ $backup.imagePullSecrets | toYaml | indent 6 }} + {{- end }} + pitr: + {{- if not $backup.pitr.enabled }} + enabled: false + {{- else }} + enabled: true + storageName: {{ $backup.pitr.storageName }} + timeBetweenUploads: {{ $backup.pitr.timeBetweenUploads }} + timeoutSeconds: {{ $backup.pitr.timeoutSeconds }} + resources: + requests: +{{ tpl ($backup.pitr.resources.requests | toYaml) $ | indent 10 }} + limits: +{{ tpl ($backup.pitr.resources.limits | toYaml) $ | indent 10 }} + {{- end }} + storages: +{{ include "pxc-database.storages" . | indent 6 }} + schedule: +{{ $backup.schedule | toYaml | indent 6 }} +{{- end }} diff --git a/charts/percona/pxc-db/1.15.1/templates/s3-secret.yaml b/charts/percona/pxc-db/1.15.1/templates/s3-secret.yaml new file mode 100644 index 000000000..b801f1890 --- /dev/null +++ b/charts/percona/pxc-db/1.15.1/templates/s3-secret.yaml @@ -0,0 +1,16 @@ +{{- range $key, $value := .Values.backup.storages }} +{{- if and (hasKey $value "type") (eq $value.type "s3") (hasKey $value "s3") (hasKey (index $value "s3") "credentialsAccessKey") (hasKey (index $value "s3") "credentialsSecretKey") }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "pxc-database.fullname" $ }}-s3-{{ $key }} + namespace: {{ $.Release.Namespace }} + labels: +{{ include "pxc-database.labels" $ | indent 4 }} +type: Opaque +data: + AWS_ACCESS_KEY_ID: {{ index $value "s3" "credentialsAccessKey" | b64enc }} + AWS_SECRET_ACCESS_KEY: {{ index $value "s3" "credentialsSecretKey" | b64enc }} +{{- end }} +{{- end }} diff --git a/charts/percona/pxc-db/1.15.1/values.yaml b/charts/percona/pxc-db/1.15.1/values.yaml new file mode 100644 index 000000000..e710e65f7 --- /dev/null +++ b/charts/percona/pxc-db/1.15.1/values.yaml @@ -0,0 +1,713 @@ +# Default values for pxc-cluster. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +finalizers: + - percona.com/delete-pxc-pods-in-order +## Set this if you want to delete proxysql persistent volumes on cluster deletion +# - percona.com/delete-proxysql-pvc +## Set this if you want to delete database persistent volumes on cluster deletion +# - percona.com/delete-pxc-pvc +## Set this if you want to delete cert manager certificates on cluster deletion +# - percona.com/delete-ssl + +nameOverride: "" +fullnameOverride: "" + +# PerconaXtraDBCluster annotations +annotations: {} + +operatorImageRepository: percona/percona-xtradb-cluster-operator + +crVersion: 1.15.1 + +enableVolumeExpansion: false + +ignoreAnnotations: [] + # - iam.amazonaws.com/role +ignoreLabels: [] + # - rack +pause: false +# initContainer: +# image: "percona/percona-xtradb-cluster-operator:1.15.1" +# resources: +# requests: +# memory: 100M +# cpu: 100m +# limits: +# memory: 200M +# cpu: 200m +# unsafeFlags: +# tls: false +# pxcSize: false +# proxySize: false +# backupIfUnhealthy: false +updateStrategy: SmartUpdate +upgradeOptions: + versionServiceEndpoint: https://check.percona.com + apply: disabled + schedule: "0 4 * * *" +enableCRValidationWebhook: false +tls: + enabled: true + # SANs: + # - pxc-1.example.com + # - pxc-2.example.com + # - pxc-3.example.com + # issuerConf: + # name: special-selfsigned-issuer + # kind: ClusterIssuer + # group: cert-manager.io + +pxc: + size: 3 + image: + repository: percona/percona-xtradb-cluster + tag: 8.0.36-28.1 + # imagePullPolicy: Always + autoRecovery: true + # expose: + # enabled: true + # type: LoadBalancer + # externalTrafficPolicy: Local + # internalTrafficPolicy: Local + # loadBalancerSourceRanges: + # - 10.0.0.0/8 + # loadBalancerIP: 127.0.0.1 + # annotations: + # networking.gke.io/load-balancer-type: "Internal" + # labels: + # rack: rack-22 + # replicationChannels: + # - name: pxc1_to_pxc2 + # isSource: true + # - name: pxc2_to_pxc1 + # isSource: false + # configuration: + # sourceRetryCount: 3 + # sourceConnectRetry: 60 + # ssl: false + # sslSkipVerify: true + # ca: '/etc/mysql/ssl/ca.crt' + # sourcesList: + # - host: 10.95.251.101 + # port: 3306 + # weight: 100 + # schedulerName: mycustom-scheduler + imagePullSecrets: [] + # - name: private-registry-credentials + annotations: {} + # iam.amazonaws.com/role: role-arn + labels: {} + # rack: rack-22 + # priorityClassName: high-priority + readinessDelaySec: 15 + livenessDelaySec: 300 + ## Uncomment to pass in a mysql config file + # configuration: | + # [mysqld] + # wsrep_debug=ON + # wsrep_provider_options="gcache.size=1G; gcache.recover=yes" + # envVarsSecret: my-env-var-secrets + resources: + requests: + memory: 1G + cpu: 600m + limits: {} + # memory: 1G + # cpu: 600m + # runtimeClassName: image-rc + sidecars: [] + sidecarVolumes: [] + sidecarPVCs: [] + sidecarResources: + requests: {} + limits: {} + nodeSelector: {} + # disktype: ssd + # topologySpreadConstraints: + # - labelSelector: + # matchLabels: + # app.kubernetes.io/name: percona-xtradb-cluster-operator + # maxSkew: 1 + # topologyKey: kubernetes.io/hostname + # whenUnsatisfiable: DoNotSchedule + affinity: + antiAffinityTopologyKey: "kubernetes.io/hostname" + # advanced: + # nodeAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # nodeSelectorTerms: + # - matchExpressions: + # - key: kubernetes.io/e2e-az-name + # operator: In + # values: + # - e2e-az1 + # - e2e-az2 + tolerations: [] + # - key: "node.alpha.kubernetes.io/unreachable" + # operator: "Exists" + # effect: "NoExecute" + # tolerationSeconds: 6000 + gracePeriod: 600 + # lifecycle: + # preStop: + # exec: + # command: [ "/bin/true" ] + # postStart: + # exec: + # command: [ "/bin/true" ] + podDisruptionBudget: + # only one of maxUnavailable or minAvaliable can be set + maxUnavailable: 1 + # minAvailable: 0 + persistence: + enabled: true + ## percona data Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # storageClass: "-" + accessMode: ReadWriteOnce + # dataSource: + # name: new-snapshot-test + # kind: VolumeSnapshot + # apiGroup: snapshot.storage.k8s.io + size: 8Gi + + # disable Helm creating TLS certificates if you want to let the operator + # request certificates from cert-manager + certManager: false + + # If this is set will not create secrets from values and will instead try to use + # a pre-existing secret of the same name. + # clusterSecretName: cluster1-secrets + readinessProbes: + initialDelaySeconds: 15 + timeoutSeconds: 15 + periodSeconds: 30 + successThreshold: 1 + failureThreshold: 5 + livenessProbes: + initialDelaySeconds: 300 + timeoutSeconds: 5 + periodSeconds: 10 + successThreshold: 1 + failureThreshold: 3 + # A custom Kubernetes Security Context for a Container to be used instead of the default one + # containerSecurityContext: + # privileged: false + # A custom Kubernetes Security Context for a Pod to be used instead of the default one + # podSecurityContext: + # fsGroup: 1001 + # supplementalGroups: + # - 1001 + # serviceAccountName: percona-xtradb-cluster-operator-workload + +haproxy: + enabled: true + size: 3 + image: percona/haproxy:2.8.5 + # imagePullPolicy: Always + imagePullSecrets: [] + # - name: private-registry-credentials +# configuration: | +# +# the actual default configuration file can be found here https://raw.githubusercontent.com/percona/percona-xtradb-cluster-operator/main/build/haproxy-global.cfg +# +# global +# maxconn 2048 +# external-check +# insecure-fork-wanted +# stats socket /etc/haproxy/pxc/haproxy.sock mode 600 expose-fd listeners level admin +# +# defaults +# default-server init-addr last,libc,none +# log global +# mode tcp +# retries 10 +# timeout client 28800s +# timeout connect 100500 +# timeout server 28800s +# +# resolvers kubernetes +# parse-resolv-conf +# +# frontend galera-in +# bind *:3309 accept-proxy +# bind *:3306 +# mode tcp +# option clitcpka +# default_backend galera-nodes +# +# frontend galera-admin-in +# bind *:33062 +# mode tcp +# option clitcpka +# default_backend galera-admin-nodes +# +# frontend galera-replica-in +# bind *:3307 +# mode tcp +# option clitcpka +# default_backend galera-replica-nodes +# +# frontend galera-mysqlx-in +# bind *:33060 +# mode tcp +# option clitcpka +# default_backend galera-mysqlx-nodes +# +# frontend stats +# bind *:8404 +# mode http +# option http-use-htx +# http-request use-service prometheus-exporter if { path /metrics } + annotations: {} + # iam.amazonaws.com/role: role-arn + labels: {} + # rack: rack-22 + # runtimeClassName: image-rc + # priorityClassName: high-priority + # schedulerName: mycustom-scheduler + readinessDelaySec: 15 + livenessDelaySec: 300 + # envVarsSecret: my-env-var-secrets + resources: + requests: + memory: 1G + cpu: 600m + limits: {} + # memory: 1G + # cpu: 600m + sidecars: [] + sidecarVolumes: [] + sidecarPVCs: [] + sidecarResources: + requests: {} + limits: {} + nodeSelector: {} + # disktype: ssd + # serviceAccountName: percona-xtradb-cluster-operator-workload + # topologySpreadConstraints: + # - labelSelector: + # matchLabels: + # app.kubernetes.io/name: percona-xtradb-cluster-operator + # maxSkew: 1 + # topologyKey: kubernetes.io/hostname + # whenUnsatisfiable: DoNotSchedule + affinity: + antiAffinityTopologyKey: "kubernetes.io/hostname" + # advanced: + # nodeAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # nodeSelectorTerms: + # - matchExpressions: + # - key: kubernetes.io/e2e-az-name + # operator: In + # values: + # - e2e-az1 + # - e2e-az2 + tolerations: [] + # - key: "node.alpha.kubernetes.io/unreachable" + # operator: "Exists" + # effect: "NoExecute" + # tolerationSeconds: 6000 + gracePeriod: 30 + # lifecycle: + # preStop: + # exec: + # command: [ "/bin/true" ] + # postStart: + # exec: + # command: [ "/bin/true" ] + # only one of `maxUnavailable` or `minAvailable` can be set. + podDisruptionBudget: + maxUnavailable: 1 + # minAvailable: 0 + readinessProbes: + initialDelaySeconds: 15 + timeoutSeconds: 1 + periodSeconds: 5 + successThreshold: 1 + failureThreshold: 3 + livenessProbes: + initialDelaySeconds: 60 + timeoutSeconds: 5 + periodSeconds: 30 + successThreshold: 1 + failureThreshold: 4 + # exposePrimary: + # enabled: false + # type: ClusterIP + # annotations: + # service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp + # externalTrafficPolicy: Cluster + # internalTrafficPolicy: Cluster + # labels: + # rack: rack-22 + # loadBalancerSourceRanges: + # - 10.0.0.0/8 + # loadBalancerIP: 127.0.0.1 + # exposeReplicas: + # enabled: true + # onlyReaders: false + # type: ClusterIP + # annotations: + # service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp + # externalTrafficPolicy: Cluster + # internalTrafficPolicy: Cluster + # labels: + # rack: rack-22 + # loadBalancerSourceRanges: + # - 10.0.0.0/8 + # loadBalancerIP: 127.0.0.1 + # A custom Kubernetes Security Context for a Container to be used instead of the default one + # containerSecurityContext: + # privileged: false + # A custom Kubernetes Security Context for a Pod to be used instead of the default one + # podSecurityContext: + # fsGroup: 1001 + # supplementalGroups: + # - 1001 + +proxysql: + enabled: false + size: 3 + image: "percona/proxysql2:2.5.5" + # imagePullPolicy: Always + imagePullSecrets: [] +# configuration: | +# datadir="/var/lib/proxysql" +# +# admin_variables = +# { +# admin_credentials="proxyadmin:admin_password" +# mysql_ifaces="0.0.0.0:6032" +# refresh_interval=2000 +# +# cluster_username="proxyadmin" +# cluster_password="admin_password" +# checksum_admin_variables=false +# checksum_ldap_variables=false +# checksum_mysql_variables=false +# cluster_check_interval_ms=200 +# cluster_check_status_frequency=100 +# cluster_mysql_query_rules_save_to_disk=true +# cluster_mysql_servers_save_to_disk=true +# cluster_mysql_users_save_to_disk=true +# cluster_proxysql_servers_save_to_disk=true +# cluster_mysql_query_rules_diffs_before_sync=1 +# cluster_mysql_servers_diffs_before_sync=1 +# cluster_mysql_users_diffs_before_sync=1 +# cluster_proxysql_servers_diffs_before_sync=1 +# } +# +# mysql_variables= +# { +# monitor_password="monitor" +# monitor_galera_healthcheck_interval=1000 +# threads=2 +# max_connections=2048 +# default_query_delay=0 +# default_query_timeout=10000 +# poll_timeout=2000 +# interfaces="0.0.0.0:3306" +# default_schema="information_schema" +# stacksize=1048576 +# connect_timeout_server=10000 +# monitor_history=60000 +# monitor_connect_interval=20000 +# monitor_ping_interval=10000 +# ping_timeout_server=200 +# commands_stats=true +# sessions_sort=true +# have_ssl=true +# ssl_p2s_ca="/etc/proxysql/ssl-internal/ca.crt" +# ssl_p2s_cert="/etc/proxysql/ssl-internal/tls.crt" +# ssl_p2s_key="/etc/proxysql/ssl-internal/tls.key" +# ssl_p2s_cipher="ECDHE-RSA-AES128-GCM-SHA256" +# } + # - name: private-registry-credentials + annotations: {} + # iam.amazonaws.com/role: role-arn + labels: {} + # rack: rack-22 + # runtimeClassName: image-rc + # expose: + # enabled: false + # type: ClusterIP + # annotations: + # service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp + # externalTrafficPolicy: Cluster + # internalTrafficPolicy: Cluster + # labels: + # rack: rack-22 + # loadBalancerSourceRanges: + # - 10.0.0.0/8 + # loadBalancerIP: 127.0.0.1 + # priorityClassName: high-priority + # schedulerName: mycustom-scheduler + readinessDelaySec: 15 + livenessDelaySec: 300 + # envVarsSecret: my-env-var-secrets + resources: + requests: + memory: 1G + cpu: 600m + limits: {} + # memory: 1G + # cpu: 600m + sidecars: [] + sidecarVolumes: [] + sidecarPVCs: [] + sidecarResources: + requests: {} + limits: {} + nodeSelector: {} + # disktype: ssd + # topologySpreadConstraints: + # - labelSelector: + # matchLabels: + # app.kubernetes.io/name: percona-xtradb-cluster-operator + # maxSkew: 1 + # topologyKey: kubernetes.io/hostname + # whenUnsatisfiable: DoNotSchedule + # serviceAccountName: percona-xtradb-cluster-operator-workload + affinity: + antiAffinityTopologyKey: "kubernetes.io/hostname" + # advanced: + # nodeAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # nodeSelectorTerms: + # - matchExpressions: + # - key: kubernetes.io/e2e-az-name + # operator: In + # values: + # - e2e-az1 + # - e2e-az2 + tolerations: [] + # - key: "node.alpha.kubernetes.io/unreachable" + # operator: "Exists" + # effect: "NoExecute" + # tolerationSeconds: 6000 + gracePeriod: 30 + # lifecycle: + # preStop: + # exec: + # command: [ "/bin/true" ] + # postStart: + # exec: + # command: [ "/bin/true" ] + # only one of `maxUnavailable` or `minAvailable` can be set. + podDisruptionBudget: + maxUnavailable: 1 + # minAvailable: 0 + persistence: + enabled: true + ## Percona data Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # storageClass: "-" + accessMode: ReadWriteOnce + size: 8Gi + # A custom Kubernetes Security Context for a Container to be used instead of the default one + # containerSecurityContext: + # privileged: false + # A custom Kubernetes Security Context for a Pod to be used instead of the default one + # podSecurityContext: + # fsGroup: 1001 + # supplementalGroups: + # - 1001 + +logcollector: + enabled: true + image: percona/percona-xtradb-cluster-operator:1.15.1-logcollector-fluentbit3.1.4 + # imagePullPolicy: Always + imagePullSecrets: [] + # configuration: | + # [OUTPUT] + # Name es + # Match * + # Host 192.168.2.3 + # Port 9200 + # Index my_index + # Type my_type + resources: + requests: + memory: 100M + cpu: 200m + limits: {} + # A custom Kubernetes Security Context for a Container to be used instead of the default one + # containerSecurityContext: + # privileged: false + +pmm: + enabled: false + image: + repository: percona/pmm-client + tag: 2.42.0 + # imagePullPolicy: Always + imagePullSecrets: [] + serverHost: monitoring-service + serverUser: admin + # pxcParams: "--disable-tablestats-limit=2000" + # proxysqlParams: "--custom-labels=CUSTOM-LABELS" + # containerSecurityContext: + # privileged: false + resources: + requests: + memory: 150M + cpu: 300m + limits: {} + +backup: + enabled: true + # allowParallel: true + image: + repository: percona/percona-xtradb-cluster-operator + tag: 1.15.1-pxc8.0-backup-pxb8.0.35 + # backoffLimit: 6 + # serviceAccountName: percona-xtradb-cluster-operator + # imagePullPolicy: Always + imagePullSecrets: [] + # - name: private-registry-credentials + pitr: + enabled: false + storageName: s3-us-west-binlogs + timeBetweenUploads: 60 + timeoutSeconds: 60 + resources: + requests: {} + limits: {} + storages: {} + # fs-pvc: + # type: filesystem + # volume: + # persistentVolumeClaim: + # storageClassName: standard + # accessModes: ["ReadWriteOnce"] + # resources: + # requests: + # storage: 6Gi + # s3-us-west: + # type: s3 + # verifyTLS: true + # nodeSelector: + # storage: tape + # backupWorker: 'True' + # resources: + # requests: + # memory: 1G + # cpu: 600m + # topologySpreadConstraints: + # - labelSelector: + # matchLabels: + # app.kubernetes.io/name: percona-xtradb-cluster-operator + # maxSkew: 1 + # topologyKey: kubernetes.io/hostname + # whenUnsatisfiable: DoNotSchedule + # affinity: + # nodeAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # nodeSelectorTerms: + # - matchExpressions: + # - key: backupWorker + # operator: In + # values: + # - 'True' + # tolerations: + # - key: "backupWorker" + # operator: "Equal" + # value: "True" + # effect: "NoSchedule" + # annotations: + # testName: scheduled-backup + # labels: + # backupWorker: 'True' + # schedulerName: 'default-scheduler' + # priorityClassName: 'high-priority' + # containerSecurityContext: + # privileged: true + # podSecurityContext: + # fsGroup: 1001 + # supplementalGroups: [1001, 1002, 1003] + # containerOptions: + # env: + # - name: VERIFY_TLS + # value: "false" + # args: + # xtrabackup: + # - "--someflag=abc" + # xbcloud: + # - "--someflag=abc" + # xbstream: + # - "--someflag=abc" + # s3: + # bucket: S3-BACKUP-BUCKET-NAME-HERE + # # Use credentialsSecret OR credentialsAccessKey/credentialsSecretKey + # credentialsSecret: my-cluster-name-backup-s3 + # #credentialsAccessKey: REPLACE-WITH-AWS-ACCESS-KEY + # #credentialsSecretKey: REPLACE-WITH-AWS-SECRET-KEY + # region: us-west-2 + # endpointUrl: https://sfo2.digitaloceanspaces.com + # s3-us-west-binlogs: + # type: s3 + # s3: + # bucket: S3-BACKUP-BUCKET-NAME-HERE/DIRECTORY + # credentialsSecret: my-cluster-name-backup-s3 + # region: us-west-2 + # endpointUrl: https://sfo2.digitaloceanspaces.com + # azure-blob: + # type: azure + # azure: + # credentialsSecret: azure-secret + # container: test + # endpointUrl: https://accountName.blob.core.windows.net + # storageClass: Hot + + schedule: [] + # - name: "daily-backup" + # schedule: "0 0 * * *" + # keep: 5 + # storageName: fs-pvc + # - name: "sat-night-backup" + # schedule: "0 0 * * 6" + # keep: 3 + # storageName: s3-us-west + +secrets: + ## You should be overriding these with your own or specify name for clusterSecretName. + # passwords: + # root: insecure-root-password + # xtrabackup: insecure-xtrabackup-password + # monitor: insecure-monitor-password + # clustercheck: insecure-clustercheck-password + # proxyadmin: insecure-proxyadmin-password + # pmmserver: insecure-pmmserver-password + # # If pmmserverkey is set in that case pmmserver pass will not be included + # # pmmserverkey: set-pmmserver-api-key + # operator: insecure-operator-password + # replication: insecure-replication-password + ## If you are using `cert-manager` you can skip this next section. + tls: {} + # This should be the name of a secret that contains certificates. + # it should have the following keys: `ca.crt`, `tls.crt`, `tls.key` + # If not set the Helm chart will attempt to create certificates + # for you [not recommended for prod]: + # cluster: + + # This should be the name of a secret that contains certificates. + # it should have the following keys: `ca.crt`, `tls.crt`, `tls.key` + # If not set the Helm chart will attempt to create certificates + # for you [not recommended for prod]: + # internal: + # logCollector: cluster1-log-collector-secrets + # vault: keyring-secret-vault diff --git a/charts/percona/pxc-operator/1.15.1/.helmignore b/charts/percona/pxc-operator/1.15.1/.helmignore new file mode 100644 index 000000000..50af03172 --- /dev/null +++ b/charts/percona/pxc-operator/1.15.1/.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/percona/pxc-operator/1.15.1/Chart.yaml b/charts/percona/pxc-operator/1.15.1/Chart.yaml new file mode 100644 index 000000000..688d138c9 --- /dev/null +++ b/charts/percona/pxc-operator/1.15.1/Chart.yaml @@ -0,0 +1,22 @@ +annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Percona Operator For MySQL based on Percona XtraDB + Cluster + catalog.cattle.io/kube-version: '>=1.21-0' + catalog.cattle.io/release-name: pxc-operator +apiVersion: v2 +appVersion: 1.15.1 +description: A Helm chart for deploying the Percona Operator for MySQL (based on Percona + XtraDB Cluster) +home: https://docs.percona.com/percona-operator-for-mysql/pxc/ +icon: file://assets/icons/pxc-operator.png +kubeVersion: '>=1.21-0' +maintainers: +- email: tomislav.plavcic@percona.com + name: tplavcic +- email: natalia.marukovich@percona.com + name: nmarukovich +- email: sergey.pronin@percona.com + name: spron-in +name: pxc-operator +version: 1.15.1 diff --git a/charts/percona/pxc-operator/1.15.1/LICENSE.txt b/charts/percona/pxc-operator/1.15.1/LICENSE.txt new file mode 100644 index 000000000..6a31453af --- /dev/null +++ b/charts/percona/pxc-operator/1.15.1/LICENSE.txt @@ -0,0 +1,13 @@ +Copyright 2019 Paul Czarkowski + +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. \ No newline at end of file diff --git a/charts/percona/pxc-operator/1.15.1/README.md b/charts/percona/pxc-operator/1.15.1/README.md new file mode 100644 index 000000000..93b4aa3f5 --- /dev/null +++ b/charts/percona/pxc-operator/1.15.1/README.md @@ -0,0 +1,64 @@ +# Percona Operator For MySQL + +[Percona XtraDB Cluster (PXC)](https://www.percona.com/doc/percona-xtradb-cluster/LATEST/index.html) is a database clustering solution for MySQL. Percona Operator For MySQL allows users to deploy and manage Percona XtraDB Clusters on Kubernetes. + +Useful links +* [Operator Github repository](https://github.com/percona/percona-xtradb-cluster-operator) +* [Operator Documentation](https://www.percona.com/doc/kubernetes-operator-for-pxc/index.html) + +## Pre-requisites +* Kubernetes 1.28+ +* Helm v3 + +# Installation + +This chart will deploy the Operator Pod for the further Percona XtraDB Cluster creation in Kubernetes. + +## Installing the Chart +To install the chart with the `pxc` release name using a dedicated namespace (recommended): + +```sh +helm repo add percona https://percona.github.io/percona-helm-charts/ +helm install my-operator percona/pxc-operator --version 1.15.1 --namespace my-namespace +``` + +The chart can be customized using the following configurable parameters: + +| Parameter | Description | Default | +| ------------------------------- | -----------------------------------------------------------------------------------------------| -------------------------------------------------| +| `image` | PXC Operator Container image full path | `percona/percona-xtradb-cluster-operator:1.15.1` | +| `imagePullPolicy` | PXC Operator Container pull policy | `Always` | +| `containerSecurityContext` | PXC Operator Container securityContext | `{}` | +| `imagePullSecrets` | PXC Operator Pod pull secret | `[]` | +| `replicaCount` | PXC Operator Pod quantity | `1` | +| `tolerations` | List of node taints to tolerate | `[]` | +| `podAnnotations` | Operator Pod user-defined annotations | `{}` | +| `resources` | Resource requests and limits | `{}` | +| `nodeSelector` | Labels for Pod assignment | `{}` | +| `logStructured` | Force PXC operator to print JSON-wrapped log messages | `false` | +| `logLevel` | PXC Operator logging level | `INFO` | +| `disableTelemetry` | Disable sending PXC Operator telemetry data to Percona | `false` | +| `watchAllNamespaces` | Watch all namespaces (Install cluster-wide) | `false` | +| `watchNamespace` | Comma separated list of namespace(s) to watch when different from release namespace | `""` | +| `createNamespace` | Create the watched namespace(s) | `false` | +| `rbac.create` | If false RBAC will not be created. RBAC resources will need to be created manually | `true` | +| `serviceAccount.create` | If false the ServiceAccounts will not be created. The ServiceAccounts must be created manually | `true` | +| `extraEnvVars` | Custom pod environment variables | `[]` | + +Specify parameters using `--set key=value[,key=value]` argument to `helm install` + +Alternatively a YAML file that specifies the values for the parameters can be provided like this: + +```sh +helm install pxc-operator -f values.yaml percona/pxc-operator +``` + +## Deploy the database + +To deploy Percona XtraDB Cluster run the following command: + +```sh +helm install my-db percona/pxc-db +``` + +See more about Percona XtraDB Cluster in its chart [here](https://github.com/percona/percona-helm-charts/blob/main/charts/pxc-db) or in the [Helm chart installation guide](https://www.percona.com/doc/kubernetes-operator-for-pxc/helm.html). diff --git a/charts/percona/pxc-operator/1.15.1/crds/crd.yaml b/charts/percona/pxc-operator/1.15.1/crds/crd.yaml new file mode 100644 index 000000000..f8d6ddc76 --- /dev/null +++ b/charts/percona/pxc-operator/1.15.1/crds/crd.yaml @@ -0,0 +1,11129 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + name: perconaxtradbclusterbackups.pxc.percona.com +spec: + group: pxc.percona.com + names: + kind: PerconaXtraDBClusterBackup + listKind: PerconaXtraDBClusterBackupList + plural: perconaxtradbclusterbackups + shortNames: + - pxc-backup + - pxc-backups + singular: perconaxtradbclusterbackup + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Cluster name + jsonPath: .spec.pxcCluster + name: Cluster + type: string + - description: Storage name from pxc spec + jsonPath: .status.storageName + name: Storage + type: string + - description: Backup destination + jsonPath: .status.destination + name: Destination + type: string + - description: Job status + jsonPath: .status.state + name: Status + type: string + - description: Completed time + jsonPath: .status.completed + name: Completed + type: date + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + priorityClassName: + type: string + schedulerName: + type: string + spec: + properties: + containerOptions: + properties: + args: + properties: + xbcloud: + items: + type: string + type: array + xbstream: + items: + type: string + type: array + xtrabackup: + items: + type: string + type: array + type: object + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + 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 + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + type: object + pxcCluster: + type: string + storageName: + type: string + type: object + status: + properties: + azure: + properties: + container: + type: string + credentialsSecret: + type: string + endpointUrl: + type: string + storageClass: + type: string + type: object + completed: + format: date-time + type: string + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + 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 + destination: + type: string + image: + type: string + lastscheduled: + format: date-time + type: string + latestRestorableTime: + format: date-time + type: string + s3: + properties: + bucket: + type: string + credentialsSecret: + type: string + endpointUrl: + type: string + region: + type: string + type: object + sslInternalSecretName: + type: string + sslSecretName: + type: string + state: + type: string + storage_type: + type: string + storageName: + type: string + vaultSecretName: + type: string + verifyTLS: + type: boolean + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + name: perconaxtradbclusterrestores.pxc.percona.com +spec: + group: pxc.percona.com + names: + kind: PerconaXtraDBClusterRestore + listKind: PerconaXtraDBClusterRestoreList + plural: perconaxtradbclusterrestores + shortNames: + - pxc-restore + - pxc-restores + singular: perconaxtradbclusterrestore + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Cluster name + jsonPath: .spec.pxcCluster + name: Cluster + type: string + - description: Job status + jsonPath: .status.state + name: Status + type: string + - description: Completed time + jsonPath: .status.completed + name: Completed + type: date + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + backupName: + type: string + backupSource: + properties: + azure: + properties: + container: + type: string + credentialsSecret: + type: string + endpointUrl: + type: string + storageClass: + type: string + type: object + completed: + format: date-time + type: string + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + 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 + destination: + type: string + image: + type: string + lastscheduled: + format: date-time + type: string + latestRestorableTime: + format: date-time + type: string + s3: + properties: + bucket: + type: string + credentialsSecret: + type: string + endpointUrl: + type: string + region: + type: string + type: object + sslInternalSecretName: + type: string + sslSecretName: + type: string + state: + type: string + storage_type: + type: string + storageName: + type: string + vaultSecretName: + type: string + verifyTLS: + type: boolean + type: object + containerOptions: + properties: + args: + properties: + xbcloud: + items: + type: string + type: array + xbstream: + items: + type: string + type: array + xtrabackup: + items: + type: string + type: array + type: object + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + 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 + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + type: object + pitr: + properties: + backupSource: + properties: + azure: + properties: + container: + type: string + credentialsSecret: + type: string + endpointUrl: + type: string + storageClass: + type: string + type: object + completed: + format: date-time + type: string + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + 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 + destination: + type: string + image: + type: string + lastscheduled: + format: date-time + type: string + latestRestorableTime: + format: date-time + type: string + s3: + properties: + bucket: + type: string + credentialsSecret: + type: string + endpointUrl: + type: string + region: + type: string + type: object + sslInternalSecretName: + type: string + sslSecretName: + type: string + state: + type: string + storage_type: + type: string + storageName: + type: string + vaultSecretName: + type: string + verifyTLS: + type: boolean + type: object + date: + type: string + gtid: + type: string + type: + type: string + type: object + pxcCluster: + type: string + resources: + properties: + claims: + items: + properties: + name: + 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 + 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 + type: object + type: object + type: object + status: + properties: + comments: + type: string + completed: + format: date-time + type: string + lastscheduled: + format: date-time + type: string + state: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + name: perconaxtradbclusters.pxc.percona.com +spec: + group: pxc.percona.com + names: + kind: PerconaXtraDBCluster + listKind: PerconaXtraDBClusterList + plural: perconaxtradbclusters + shortNames: + - pxc + - pxcs + singular: perconaxtradbcluster + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.host + name: ENDPOINT + type: string + - jsonPath: .status.state + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + deprecated: true + deprecationWarning: pxc.percona.com/v1-2-0 PerconaXtraDBCluster is deprecated + and will be removed in v1.16.0; see v1.12.0 release notes for instructions to + migrate to pxc.percona.com/v1 + name: v1-2-0 + schema: + openAPIV3Schema: + properties: + spec: + type: object + x-kubernetes-preserve-unknown-fields: true + status: + type: object + x-kubernetes-preserve-unknown-fields: true + type: object + served: false + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.host + name: ENDPOINT + type: string + - jsonPath: .status.state + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + deprecated: true + deprecationWarning: pxc.percona.com/v1-3-0 PerconaXtraDBCluster is deprecated + and will be removed in v1.16.0; see v1.12.0 release notes for instructions to + migrate to pxc.percona.com/v1 + name: v1-3-0 + schema: + openAPIV3Schema: + properties: + spec: + type: object + x-kubernetes-preserve-unknown-fields: true + status: + type: object + x-kubernetes-preserve-unknown-fields: true + type: object + served: false + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.host + name: ENDPOINT + type: string + - jsonPath: .status.state + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + deprecated: true + deprecationWarning: pxc.percona.com/v1-4-0 PerconaXtraDBCluster is deprecated + and will be removed in v1.16.0; see v1.12.0 release notes for instructions to + migrate to pxc.percona.com/v1 + name: v1-4-0 + schema: + openAPIV3Schema: + properties: + spec: + type: object + x-kubernetes-preserve-unknown-fields: true + status: + type: object + x-kubernetes-preserve-unknown-fields: true + type: object + served: false + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.host + name: ENDPOINT + type: string + - jsonPath: .status.state + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + deprecated: true + deprecationWarning: pxc.percona.com/v1-5-0 PerconaXtraDBCluster is deprecated + and will be removed in v1.16.0; see v1.12.0 release notes for instructions to + migrate to pxc.percona.com/v1 + name: v1-5-0 + schema: + openAPIV3Schema: + properties: + spec: + type: object + x-kubernetes-preserve-unknown-fields: true + status: + type: object + x-kubernetes-preserve-unknown-fields: true + type: object + served: false + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.host + name: ENDPOINT + type: string + - jsonPath: .status.state + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + deprecated: true + deprecationWarning: pxc.percona.com/v1-6-0 PerconaXtraDBCluster is deprecated + and will be removed in v1.16.0; see v1.12.0 release notes for instructions to + migrate to pxc.percona.com/v1 + name: v1-6-0 + schema: + openAPIV3Schema: + properties: + spec: + type: object + x-kubernetes-preserve-unknown-fields: true + status: + type: object + x-kubernetes-preserve-unknown-fields: true + type: object + served: false + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.host + name: ENDPOINT + type: string + - jsonPath: .status.state + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + deprecated: true + deprecationWarning: pxc.percona.com/v1-7-0 PerconaXtraDBCluster is deprecated + and will be removed in v1.16.0; see v1.12.0 release notes for instructions to + migrate to pxc.percona.com/v1 + name: v1-7-0 + schema: + openAPIV3Schema: + properties: + spec: + type: object + x-kubernetes-preserve-unknown-fields: true + status: + type: object + x-kubernetes-preserve-unknown-fields: true + type: object + served: false + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.host + name: ENDPOINT + type: string + - jsonPath: .status.state + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + deprecated: true + deprecationWarning: pxc.percona.com/v1-8-0 PerconaXtraDBCluster is deprecated + and will be removed in v1.16.0; see v1.12.0 release notes for instructions to + migrate to pxc.percona.com/v1 + name: v1-8-0 + schema: + openAPIV3Schema: + properties: + spec: + type: object + x-kubernetes-preserve-unknown-fields: true + status: + type: object + x-kubernetes-preserve-unknown-fields: true + type: object + served: false + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.host + name: ENDPOINT + type: string + - jsonPath: .status.state + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + deprecated: true + deprecationWarning: pxc.percona.com/v1-9-0 PerconaXtraDBCluster is deprecated + and will be removed in v1.16.0; see v1.12.0 release notes for instructions to + migrate to pxc.percona.com/v1 + name: v1-9-0 + schema: + openAPIV3Schema: + properties: + spec: + type: object + x-kubernetes-preserve-unknown-fields: true + status: + type: object + x-kubernetes-preserve-unknown-fields: true + type: object + served: false + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.host + name: ENDPOINT + type: string + - jsonPath: .status.state + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + deprecated: true + deprecationWarning: pxc.percona.com/v1-10-0 PerconaXtraDBCluster is deprecated + and will be removed in v1.16.0; see v1.12.0 release notes for instructions to + migrate to pxc.percona.com/v1 + name: v1-10-0 + schema: + openAPIV3Schema: + properties: + spec: + type: object + x-kubernetes-preserve-unknown-fields: true + status: + type: object + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.host + name: ENDPOINT + type: string + - jsonPath: .status.state + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + deprecated: true + deprecationWarning: pxc.percona.com/v1-11-0 PerconaXtraDBCluster is deprecated + and will be removed in v1.16.0; see v1.12.0 release notes for instructions to + migrate to pxc.percona.com/v1 + name: v1-11-0 + schema: + openAPIV3Schema: + properties: + spec: + type: object + x-kubernetes-preserve-unknown-fields: true + status: + type: object + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.host + name: Endpoint + type: string + - jsonPath: .status.state + name: Status + type: string + - description: Ready pxc nodes + jsonPath: .status.pxc.ready + name: PXC + type: string + - description: Ready proxysql nodes + jsonPath: .status.proxysql.ready + name: proxysql + type: string + - description: Ready haproxy nodes + jsonPath: .status.haproxy.ready + name: haproxy + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + allowUnsafeConfigurations: + type: boolean + backup: + properties: + allowParallel: + type: boolean + annotations: + additionalProperties: + type: string + type: object + backoffLimit: + format: int32 + type: integer + image: + type: string + imagePullPolicy: + type: string + imagePullSecrets: + items: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + type: array + pitr: + properties: + enabled: + type: boolean + resources: + properties: + claims: + items: + properties: + name: + 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 + 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 + type: object + type: object + storageName: + type: string + timeBetweenUploads: + type: number + timeoutSeconds: + type: number + type: object + schedule: + items: + properties: + keep: + type: integer + name: + type: string + schedule: + type: string + storageName: + type: string + required: + - name + - schedule + - storageName + type: object + type: array + serviceAccountName: + type: string + storages: + additionalProperties: + properties: + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + annotations: + additionalProperties: + type: string + type: object + azure: + properties: + container: + type: string + credentialsSecret: + type: string + endpointUrl: + type: string + storageClass: + type: string + type: object + containerOptions: + properties: + args: + properties: + xbcloud: + items: + type: string + type: array + xbstream: + items: + type: string + type: array + xtrabackup: + items: + type: string + type: array + type: object + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + 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 + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + type: object + containerSecurityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + labels: + additionalProperties: + type: string + type: object + nodeSelector: + additionalProperties: + type: string + type: object + podSecurityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + priorityClassName: + type: string + resources: + properties: + claims: + items: + properties: + name: + 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 + 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 + type: object + type: object + runtimeClassName: + type: string + s3: + properties: + bucket: + type: string + credentialsSecret: + type: string + endpointUrl: + type: string + region: + type: string + type: object + schedulerName: + type: string + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + topologySpreadConstraints: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + format: int32 + type: integer + minDomains: + format: int32 + type: integer + nodeAffinityPolicy: + type: string + nodeTaintsPolicy: + type: string + topologyKey: + type: string + whenUnsatisfiable: + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + type: + type: string + verifyTLS: + type: boolean + volume: + properties: + emptyDir: + properties: + medium: + type: string + sizeLimit: + 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 + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + persistentVolumeClaim: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + 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 + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + type: object + type: object + type: object + type: object + crVersion: + type: string + enableCRValidationWebhook: + type: boolean + enableVolumeExpansion: + type: boolean + haproxy: + properties: + affinity: + properties: + advanced: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + antiAffinityTopologyKey: + type: string + type: object + annotations: + additionalProperties: + type: string + type: object + configuration: + type: string + containerSecurityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + enabled: + type: boolean + envVarsSecret: + type: string + exposePrimary: + properties: + annotations: + additionalProperties: + type: string + type: object + enabled: + type: boolean + externalTrafficPolicy: + type: string + internalTrafficPolicy: + type: string + labels: + additionalProperties: + type: string + type: object + loadBalancerIP: + type: string + loadBalancerSourceRanges: + items: + type: string + type: array + trafficPolicy: + type: string + type: + type: string + type: object + exposeReplicas: + properties: + annotations: + additionalProperties: + type: string + type: object + enabled: + type: boolean + externalTrafficPolicy: + type: string + internalTrafficPolicy: + type: string + labels: + additionalProperties: + type: string + type: object + loadBalancerIP: + type: string + loadBalancerSourceRanges: + items: + type: string + type: array + onlyReaders: + type: boolean + trafficPolicy: + type: string + type: + type: string + type: object + externalTrafficPolicy: + type: string + forceUnsafeBootstrap: + type: boolean + gracePeriod: + format: int64 + type: integer + hookScript: + type: string + image: + type: string + imagePullPolicy: + type: string + imagePullSecrets: + items: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + type: array + labels: + additionalProperties: + type: string + type: object + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessDelaySec: + format: int32 + type: integer + livenessProbes: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + loadBalancerIP: + type: string + loadBalancerSourceRanges: + items: + type: string + type: array + nodeSelector: + additionalProperties: + type: string + type: object + podDisruptionBudget: + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + minAvailable: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + type: object + podSecurityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + priorityClassName: + type: string + readinessDelaySec: + format: int32 + type: integer + readinessProbes: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + replicasExternalTrafficPolicy: + type: string + replicasLoadBalancerIP: + type: string + replicasLoadBalancerSourceRanges: + items: + type: string + type: array + replicasServiceAnnotations: + additionalProperties: + type: string + type: object + replicasServiceEnabled: + type: boolean + replicasServiceLabels: + additionalProperties: + type: string + type: object + replicasServiceType: + type: string + resources: + properties: + claims: + items: + properties: + name: + 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 + 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 + type: object + type: object + runtimeClassName: + type: string + schedulerName: + type: string + serviceAccountName: + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + serviceLabels: + additionalProperties: + type: string + type: object + serviceType: + type: string + sidecarPVCs: + items: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + 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 + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + status: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + allocatedResourceStatuses: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: granular + allocatedResources: + 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 + type: object + capacity: + 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 + type: object + conditions: + items: + properties: + lastProbeTime: + format: date-time + type: string + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + currentVolumeAttributesClassName: + type: string + modifyVolumeStatus: + properties: + status: + type: string + targetVolumeAttributesClassName: + type: string + required: + - status + type: object + phase: + type: string + type: object + type: object + type: array + sidecarResources: + properties: + claims: + items: + properties: + name: + 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 + 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 + type: object + type: object + sidecarVolumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + type: string + kind: + type: string + readOnly: + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + 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 + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + 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 + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + 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 + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + 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 + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + type: string + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + sidecars: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + 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 + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + 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 + 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 + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + size: + format: int32 + type: integer + sslInternalSecretName: + type: string + sslSecretName: + type: string + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + topologySpreadConstraints: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + format: int32 + type: integer + minDomains: + format: int32 + type: integer + nodeAffinityPolicy: + type: string + nodeTaintsPolicy: + type: string + topologyKey: + type: string + whenUnsatisfiable: + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + vaultSecretName: + type: string + volumeSpec: + properties: + emptyDir: + properties: + medium: + type: string + sizeLimit: + 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 + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + persistentVolumeClaim: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + 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 + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + type: object + type: object + ignoreAnnotations: + items: + type: string + type: array + ignoreLabels: + items: + type: string + type: array + initContainer: + properties: + image: + type: string + resources: + properties: + claims: + items: + properties: + name: + 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 + 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 + type: object + type: object + type: object + initImage: + type: string + logCollectorSecretName: + type: string + logcollector: + properties: + configuration: + type: string + containerSecurityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + enabled: + type: boolean + hookScript: + type: string + image: + type: string + imagePullPolicy: + type: string + resources: + properties: + claims: + items: + properties: + name: + 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 + 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 + type: object + type: object + runtimeClassName: + type: string + type: object + pause: + type: boolean + platform: + type: string + pmm: + properties: + containerSecurityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + enabled: + type: boolean + image: + type: string + imagePullPolicy: + type: string + proxysqlParams: + type: string + pxcParams: + type: string + resources: + properties: + claims: + items: + properties: + name: + 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 + 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 + type: object + type: object + runtimeClassName: + type: string + serverHost: + type: string + serverUser: + type: string + type: object + proxysql: + properties: + affinity: + properties: + advanced: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + antiAffinityTopologyKey: + type: string + type: object + annotations: + additionalProperties: + type: string + type: object + configuration: + type: string + containerSecurityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + enabled: + type: boolean + envVarsSecret: + type: string + expose: + properties: + annotations: + additionalProperties: + type: string + type: object + enabled: + type: boolean + externalTrafficPolicy: + type: string + internalTrafficPolicy: + type: string + labels: + additionalProperties: + type: string + type: object + loadBalancerIP: + type: string + loadBalancerSourceRanges: + items: + type: string + type: array + trafficPolicy: + type: string + type: + type: string + type: object + externalTrafficPolicy: + type: string + forceUnsafeBootstrap: + type: boolean + gracePeriod: + format: int64 + type: integer + hookScript: + type: string + image: + type: string + imagePullPolicy: + type: string + imagePullSecrets: + items: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + type: array + labels: + additionalProperties: + type: string + type: object + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessDelaySec: + format: int32 + type: integer + livenessProbes: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + loadBalancerIP: + type: string + loadBalancerSourceRanges: + items: + type: string + type: array + nodeSelector: + additionalProperties: + type: string + type: object + podDisruptionBudget: + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + minAvailable: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + type: object + podSecurityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + priorityClassName: + type: string + readinessDelaySec: + format: int32 + type: integer + readinessProbes: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + replicasExternalTrafficPolicy: + type: string + replicasServiceAnnotations: + additionalProperties: + type: string + type: object + replicasServiceLabels: + additionalProperties: + type: string + type: object + replicasServiceType: + type: string + resources: + properties: + claims: + items: + properties: + name: + 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 + 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 + type: object + type: object + runtimeClassName: + type: string + schedulerName: + type: string + serviceAccountName: + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + serviceLabels: + additionalProperties: + type: string + type: object + serviceType: + type: string + sidecarPVCs: + items: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + 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 + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + status: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + allocatedResourceStatuses: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: granular + allocatedResources: + 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 + type: object + capacity: + 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 + type: object + conditions: + items: + properties: + lastProbeTime: + format: date-time + type: string + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + currentVolumeAttributesClassName: + type: string + modifyVolumeStatus: + properties: + status: + type: string + targetVolumeAttributesClassName: + type: string + required: + - status + type: object + phase: + type: string + type: object + type: object + type: array + sidecarResources: + properties: + claims: + items: + properties: + name: + 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 + 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 + type: object + type: object + sidecarVolumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + type: string + kind: + type: string + readOnly: + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + 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 + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + 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 + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + 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 + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + 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 + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + type: string + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + sidecars: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + 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 + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + 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 + 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 + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + size: + format: int32 + type: integer + sslInternalSecretName: + type: string + sslSecretName: + type: string + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + topologySpreadConstraints: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + format: int32 + type: integer + minDomains: + format: int32 + type: integer + nodeAffinityPolicy: + type: string + nodeTaintsPolicy: + type: string + topologyKey: + type: string + whenUnsatisfiable: + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + vaultSecretName: + type: string + volumeSpec: + properties: + emptyDir: + properties: + medium: + type: string + sizeLimit: + 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 + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + persistentVolumeClaim: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + 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 + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + type: object + type: object + pxc: + properties: + affinity: + properties: + advanced: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + antiAffinityTopologyKey: + type: string + type: object + annotations: + additionalProperties: + type: string + type: object + autoRecovery: + type: boolean + configuration: + type: string + containerSecurityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + enabled: + type: boolean + envVarsSecret: + type: string + expose: + properties: + annotations: + additionalProperties: + type: string + type: object + enabled: + type: boolean + externalTrafficPolicy: + type: string + internalTrafficPolicy: + type: string + labels: + additionalProperties: + type: string + type: object + loadBalancerIP: + type: string + loadBalancerSourceRanges: + items: + type: string + type: array + trafficPolicy: + type: string + type: + type: string + type: object + externalTrafficPolicy: + type: string + forceUnsafeBootstrap: + type: boolean + gracePeriod: + format: int64 + type: integer + hookScript: + type: string + image: + type: string + imagePullPolicy: + type: string + imagePullSecrets: + items: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + type: array + labels: + additionalProperties: + type: string + type: object + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessDelaySec: + format: int32 + type: integer + livenessProbes: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + loadBalancerIP: + type: string + loadBalancerSourceRanges: + items: + type: string + type: array + nodeSelector: + additionalProperties: + type: string + type: object + podDisruptionBudget: + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + minAvailable: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + type: object + podSecurityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + priorityClassName: + type: string + readinessDelaySec: + format: int32 + type: integer + readinessProbes: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + replicasExternalTrafficPolicy: + type: string + replicasServiceAnnotations: + additionalProperties: + type: string + type: object + replicasServiceLabels: + additionalProperties: + type: string + type: object + replicasServiceType: + type: string + replicationChannels: + items: + properties: + configuration: + properties: + ca: + type: string + sourceConnectRetry: + type: integer + sourceRetryCount: + type: integer + ssl: + type: boolean + sslSkipVerify: + type: boolean + type: object + isSource: + type: boolean + name: + type: string + sourcesList: + items: + properties: + host: + type: string + port: + type: integer + weight: + type: integer + type: object + type: array + type: object + type: array + resources: + properties: + claims: + items: + properties: + name: + 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 + 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 + type: object + type: object + runtimeClassName: + type: string + schedulerName: + type: string + serviceAccountName: + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + serviceLabels: + additionalProperties: + type: string + type: object + serviceType: + type: string + sidecarPVCs: + items: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + 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 + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + status: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + allocatedResourceStatuses: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: granular + allocatedResources: + 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 + type: object + capacity: + 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 + type: object + conditions: + items: + properties: + lastProbeTime: + format: date-time + type: string + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + currentVolumeAttributesClassName: + type: string + modifyVolumeStatus: + properties: + status: + type: string + targetVolumeAttributesClassName: + type: string + required: + - status + type: object + phase: + type: string + type: object + type: object + type: array + sidecarResources: + properties: + claims: + items: + properties: + name: + 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 + 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 + type: object + type: object + sidecarVolumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + type: string + kind: + type: string + readOnly: + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + 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 + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + 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 + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + 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 + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + 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 + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + type: string + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + sidecars: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + 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 + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + 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 + 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 + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + size: + format: int32 + type: integer + sslInternalSecretName: + type: string + sslSecretName: + type: string + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + topologySpreadConstraints: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + format: int32 + type: integer + minDomains: + format: int32 + type: integer + nodeAffinityPolicy: + type: string + nodeTaintsPolicy: + type: string + topologyKey: + type: string + whenUnsatisfiable: + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + vaultSecretName: + type: string + volumeSpec: + properties: + emptyDir: + properties: + medium: + type: string + sizeLimit: + 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 + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + persistentVolumeClaim: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + 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 + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + 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 + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + type: object + type: object + secretsName: + type: string + sslInternalSecretName: + type: string + sslSecretName: + type: string + tls: + properties: + SANs: + items: + type: string + type: array + enabled: + type: boolean + issuerConf: + properties: + group: + type: string + kind: + type: string + name: + type: string + required: + - name + type: object + type: object + unsafeFlags: + properties: + backupIfUnhealthy: + type: boolean + proxySize: + type: boolean + pxcSize: + type: boolean + tls: + type: boolean + type: object + updateStrategy: + type: string + upgradeOptions: + properties: + apply: + type: string + schedule: + type: string + versionServiceEndpoint: + type: string + type: object + vaultSecretName: + type: string + type: object + status: + properties: + backup: + properties: + image: + type: string + labelSelectorPath: + type: string + message: + type: string + status: + type: string + version: + type: string + type: object + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + type: object + type: array + haproxy: + properties: + image: + type: string + labelSelectorPath: + type: string + message: + type: string + ready: + format: int32 + type: integer + size: + format: int32 + type: integer + status: + type: string + version: + type: string + type: object + host: + type: string + logcollector: + properties: + image: + type: string + labelSelectorPath: + type: string + message: + type: string + status: + type: string + version: + type: string + type: object + message: + items: + type: string + type: array + observedGeneration: + format: int64 + type: integer + pmm: + properties: + image: + type: string + labelSelectorPath: + type: string + message: + type: string + status: + type: string + version: + type: string + type: object + proxysql: + properties: + image: + type: string + labelSelectorPath: + type: string + message: + type: string + ready: + format: int32 + type: integer + size: + format: int32 + type: integer + status: + type: string + version: + type: string + type: object + pxc: + properties: + image: + type: string + labelSelectorPath: + type: string + message: + type: string + ready: + format: int32 + type: integer + size: + format: int32 + type: integer + status: + type: string + version: + type: string + type: object + pxcReplication: + properties: + replicationChannels: + items: + properties: + ca: + type: string + name: + type: string + sourceConnectRetry: + type: integer + sourceRetryCount: + type: integer + ssl: + type: boolean + sslSkipVerify: + type: boolean + type: object + type: array + type: object + ready: + format: int32 + type: integer + size: + format: int32 + type: integer + state: + type: string + type: object + type: object + x-kubernetes-preserve-unknown-fields: true + served: true + storage: true + subresources: + scale: + labelSelectorPath: .status.pxc.labelSelectorPath + specReplicasPath: .spec.pxc.size + statusReplicasPath: .status.pxc.size + status: {} diff --git a/charts/percona/pxc-operator/1.15.1/templates/NOTES.txt b/charts/percona/pxc-operator/1.15.1/templates/NOTES.txt new file mode 100644 index 000000000..619fbd160 --- /dev/null +++ b/charts/percona/pxc-operator/1.15.1/templates/NOTES.txt @@ -0,0 +1,16 @@ +1. Percona Operator for MySQL is deployed. + + Check if operator Pod is running: + + kubectl get pods -l app.kubernetes.io/name={{ template "pxc-operator.name" . }} --namespace {{ .Release.Namespace }} + + Troubleshoot by checking the logs: + + export POD=$(kubectl get pods -l app.kubernetes.io/name={{ template "pxc-operator.name" . }} --namespace {{ .Release.Namespace }} --output name) + kubectl logs $POD --namespace={{ .Release.Namespace }} + +2. Deploy the cluster with the following command: + + helm install my-db percona/pxc-db --namespace={{ .Release.Namespace }} + +Read more in our documentation: https://docs.percona.com/percona-operator-for-mysql/pxc/ diff --git a/charts/percona/pxc-operator/1.15.1/templates/_helpers.tpl b/charts/percona/pxc-operator/1.15.1/templates/_helpers.tpl new file mode 100644 index 000000000..2c3d51546 --- /dev/null +++ b/charts/percona/pxc-operator/1.15.1/templates/_helpers.tpl @@ -0,0 +1,56 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "pxc-operator.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 "pxc-operator.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 "pxc-operator.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Common labels +*/}} +{{- define "pxc-operator.labels" -}} +app.kubernetes.io/name: {{ include "pxc-operator.name" . }} +helm.sh/chart: {{ include "pxc-operator.chart" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} + +{{/* +Functions returns image URI according to parameters set +*/}} +{{- define "pxc-operator.image" -}} +{{- if .Values.image }} +{{- .Values.image }} +{{- else }} +{{- printf "%s:%s" .Values.operatorImageRepository .Chart.AppVersion }} +{{- end }} +{{- end -}} \ No newline at end of file diff --git a/charts/percona/pxc-operator/1.15.1/templates/deployment.yaml b/charts/percona/pxc-operator/1.15.1/templates/deployment.yaml new file mode 100644 index 000000000..5cb8530d4 --- /dev/null +++ b/charts/percona/pxc-operator/1.15.1/templates/deployment.yaml @@ -0,0 +1,119 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "pxc-operator.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: +{{ include "pxc-operator.labels" . | indent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app.kubernetes.io/component: operator + app.kubernetes.io/name: {{ include "pxc-operator.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/part-of: {{ include "pxc-operator.name" . }} + strategy: + rollingUpdate: + maxUnavailable: 1 + type: RollingUpdate + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + app.kubernetes.io/component: operator + app.kubernetes.io/name: {{ include "pxc-operator.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/part-of: {{ include "pxc-operator.name" . }} + spec: + serviceAccountName: {{ include "pxc-operator.fullname" . }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + terminationGracePeriodSeconds: 600 + containers: + - name: percona-xtradb-cluster-operator + image: {{ include "pxc-operator.image" . }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + ports: + - containerPort: 8080 + name: metrics + protocol: TCP + command: + - percona-xtradb-cluster-operator + {{- if .Values.containerSecurityContext.readOnlyRootFilesystem }} + volumeMounts: + - name: tmpdir + mountPath: /tmp + {{- end }} + env: + - name: WATCH_NAMESPACE + {{- if .Values.watchAllNamespaces }} + value: "" + {{- else }} + value: "{{ default .Release.Namespace .Values.watchNamespace }}" + {{- end }} + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: OPERATOR_NAME + value: {{ include "pxc-operator.fullname" . }} + - name: LOG_STRUCTURED + value: "{{ .Values.logStructured }}" + - name: LOG_LEVEL + value: "{{ .Values.logLevel }}" + - name: DISABLE_TELEMETRY + value: "{{ .Values.disableTelemetry }}" + {{- if .Values.extraEnvVars }} + {{- toYaml .Values.extraEnvVars | nindent 12 }} + {{- end }} + livenessProbe: + failureThreshold: 3 + httpGet: + path: /metrics + port: metrics + scheme: HTTP + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- with .Values.containerSecurityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.containerSecurityContext.readOnlyRootFilesystem }} + volumes: + - name: tmpdir + emptyDir: {} + {{- end }} +{{- if or .Values.watchNamespace .Values.watchAllNamespaces }} +--- +apiVersion: v1 +kind: Service +metadata: + name: percona-xtradb-cluster-operator + namespace: {{ .Release.Namespace }} + labels: + name: percona-xtradb-cluster-operator +spec: + ports: + - port: 443 + targetPort: 9443 + selector: + app.kubernetes.io/name: {{ include "pxc-operator.name" . }} +{{- end }} diff --git a/charts/percona/pxc-operator/1.15.1/templates/namespace.yaml b/charts/percona/pxc-operator/1.15.1/templates/namespace.yaml new file mode 100644 index 000000000..cfc96d4d9 --- /dev/null +++ b/charts/percona/pxc-operator/1.15.1/templates/namespace.yaml @@ -0,0 +1,11 @@ +{{ if and .Values.watchNamespace .Values.createNamespace }} +{{ range ( split "," .Values.watchNamespace ) }} +apiVersion: v1 +kind: Namespace +metadata: + name: {{ trim . }} + annotations: + helm.sh/resource-policy: keep +--- +{{ end }} +{{ end }} diff --git a/charts/percona/pxc-operator/1.15.1/templates/role-binding.yaml b/charts/percona/pxc-operator/1.15.1/templates/role-binding.yaml new file mode 100644 index 000000000..a4768544f --- /dev/null +++ b/charts/percona/pxc-operator/1.15.1/templates/role-binding.yaml @@ -0,0 +1,37 @@ +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "pxc-operator.fullname" . }} + namespace: {{ .Release.Namespace }} +--- +{{- end }} +{{- if .Values.rbac.create }} +{{- if or .Values.watchNamespace .Values.watchAllNamespaces }} +kind: ClusterRoleBinding +{{- else }} +kind: RoleBinding +{{- end }} +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "pxc-operator.fullname" . }} + {{- if not (or .Values.watchNamespace .Values.watchAllNamespaces) }} + namespace: {{ .Release.Namespace }} + {{- end }} + labels: +{{ include "pxc-operator.labels" . | indent 4 }} +subjects: +- kind: ServiceAccount + name: {{ include "pxc-operator.fullname" . }} + {{- if or .Values.watchNamespace .Values.watchAllNamespaces }} + namespace: {{ .Release.Namespace }} + {{- end }} +roleRef: + {{- if or .Values.watchNamespace .Values.watchAllNamespaces }} + kind: ClusterRole + {{- else }} + kind: Role + {{- end }} + name: {{ include "pxc-operator.fullname" . }} + apiGroup: rbac.authorization.k8s.io +{{- end }} diff --git a/charts/percona/pxc-operator/1.15.1/templates/role.yaml b/charts/percona/pxc-operator/1.15.1/templates/role.yaml new file mode 100644 index 000000000..c1aeb0e30 --- /dev/null +++ b/charts/percona/pxc-operator/1.15.1/templates/role.yaml @@ -0,0 +1,142 @@ +{{- if .Values.rbac.create }} +{{- if or .Values.watchNamespace .Values.watchAllNamespaces }} +kind: ClusterRole +{{- else }} +kind: Role +{{- end }} +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "pxc-operator.fullname" . }} + {{- if not (or .Values.watchNamespace .Values.watchAllNamespaces) }} + namespace: {{ .Release.Namespace }} + {{- end }} + labels: +{{ include "pxc-operator.labels" . | indent 4 }} +rules: +- apiGroups: + - pxc.percona.com + resources: + - perconaxtradbclusters + - perconaxtradbclusters/status + - perconaxtradbclusterbackups + - perconaxtradbclusterbackups/status + - perconaxtradbclusterrestores + - perconaxtradbclusterrestores/status + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +{{- if or .Values.watchNamespace .Values.watchAllNamespaces }} +- apiGroups: + - admissionregistration.k8s.io + resources: + - validatingwebhookconfigurations + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +{{- end }} +- apiGroups: + - "" + resources: + - pods + - pods/exec + - pods/log + - configmaps + - services + - persistentvolumeclaims + - secrets + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - apps + resources: + - deployments + - replicasets + - statefulsets + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - batch + resources: + - jobs + - cronjobs + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - policy + resources: + - poddisruptionbudgets + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - events.k8s.io + - "" + resources: + - events + verbs: + - create + - patch + - get + - list + - watch +- apiGroups: + - certmanager.k8s.io + - cert-manager.io + resources: + - issuers + - certificates + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - deletecollection +{{- end }} diff --git a/charts/percona/pxc-operator/1.15.1/values.yaml b/charts/percona/pxc-operator/1.15.1/values.yaml new file mode 100644 index 000000000..719d77c5a --- /dev/null +++ b/charts/percona/pxc-operator/1.15.1/values.yaml @@ -0,0 +1,69 @@ +# Default values for pxc-operator. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +operatorImageRepository: percona/percona-xtradb-cluster-operator +imagePullPolicy: IfNotPresent +image: "" + +# set if you want to specify a namespace to watch +# defaults to `.Release.namespace` if left blank +# multiple namespaces can be specified and separated by comma +# watchNamespace: +# set if you want that watched namespaces are created by helm +# createNamespace: false + +# set if operator should be deployed in cluster wide mode. defaults to false +watchAllNamespaces: false + +# rbac: settings for deployer RBAC creation +rbac: + # rbac.create: if false RBAC resources should be in place + create: true + +# serviceAccount: settings for Service Accounts used by the deployer +serviceAccount: + # serviceAccount.create: Whether to create the Service Accounts or not + create: true + +# set if you want to use a different operator name +# defaults to `percona-xtradb-cluster-operator` +# operatorName: + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +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 don't want to specify resources, comment the following + # lines and add the curly braces after 'resources:'. + limits: + cpu: 200m + memory: 500Mi + requests: + cpu: 100m + memory: 20Mi + +containerSecurityContext: {} + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +podAnnotations: {} + +logStructured: false +logLevel: "INFO" +disableTelemetry: false + +extraEnvVars: [] +# - name: http_proxy +# value: "example-proxy-http" +# - name: https_proxy +# value: "example-proxy-https" diff --git a/index.yaml b/index.yaml index e8f352ad8..2c222b96d 100644 --- a/index.yaml +++ b/index.yaml @@ -10618,6 +10618,33 @@ entries: - assets/dh2i/dxoperator-1.0.1.tgz version: 1.0.1 dynatrace-operator: + - annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Dynatrace Operator + catalog.cattle.io/kube-version: '>=1.19.0-0' + catalog.cattle.io/release-name: dynatrace-operator + apiVersion: v2 + appVersion: 1.3.2 + created: "2024-10-25T00:36:08.437935249Z" + description: The Dynatrace Operator Helm chart for Kubernetes and OpenShift + digest: 5d8bf70cc931b891288faf2959c31aa9d8ef574b384e7e74df7d0e0e300061f8 + home: https://www.dynatrace.com/ + icon: file://assets/icons/dynatrace-operator.png + kubeVersion: '>=1.19.0-0' + maintainers: + - email: marcell.sevcsik@dynatrace.com + name: 0sewa0 + - email: christoph.muellner@dynatrace.com + name: chrismuellner + - email: lukas.hinterreiter@dynatrace.com + name: luhi-DT + name: dynatrace-operator + sources: + - https://github.com/Dynatrace/dynatrace-operator + type: application + urls: + - assets/dynatrace/dynatrace-operator-1.3.2.tgz + version: 1.3.2 - annotations: catalog.cattle.io/certified: partner catalog.cattle.io/display-name: Dynatrace Operator @@ -12178,6 +12205,36 @@ entries: - assets/linux-polska/ezd-crd-1.3.1.tgz version: 1.3.1 f5-bigip-ctlr: + - annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: F5 Container Ingress Services for Kubernetes + and OpenShift + catalog.cattle.io/kube-version: '>=1.20-0' + catalog.cattle.io/release-name: f5-bigip-ctlr + apiVersion: v1 + created: "2024-10-25T00:36:08.551733595Z" + description: Deploy the F5 Networks BIG-IP Controller for Kubernetes and OpenShift + (k8s-bigip-ctlr). + digest: 5ceba81fee30bb61a1506fbd08629d3e86a128aaccea54d317b87e2c22ac8718 + home: https://www.f5.com/products/automation-and-orchestration/container-ingress-services + icon: file://assets/icons/f5-bigip-ctlr.png + keywords: + - F5 + - BIG-IP + - Containers + - Kubernetes + - OpenShift + kubeVersion: '>=1.20-0' + maintainers: + - email: f5_cis_operators@f5.com + name: F5CISSupport + name: f5-bigip-ctlr + sources: + - https://github.com/F5Networks/k8s-bigip-ctlr + - https://github.com/F5Networks/charts + urls: + - assets/f5/f5-bigip-ctlr-0.0.3301.tgz + version: 0.0.3301 - annotations: catalog.cattle.io/certified: partner catalog.cattle.io/display-name: F5 Container Ingress Services for Kubernetes @@ -24660,6 +24717,38 @@ entries: catalog.cattle.io/kube-version: '>=1.22.0-0' catalog.cattle.io/release-name: linkerd-control-plane apiVersion: v2 + appVersion: edge-24.10.4 + created: "2024-10-25T00:36:07.876082473Z" + dependencies: + - name: partials + repository: file://../partials + version: 0.1.0 + description: 'Linkerd gives you observability, reliability, and security for your + microservices — with no code change required. ' + digest: ceb4b195b9886bf029d7e88869ca175f4636291765176775e83f2a7a882d0c1d + home: https://linkerd.io + icon: file://assets/icons/linkerd-control-plane.png + keywords: + - service-mesh + kubeVersion: '>=1.22.0-0' + maintainers: + - email: cncf-linkerd-dev@lists.cncf.io + name: Linkerd authors + url: https://linkerd.io/ + name: linkerd-control-plane + sources: + - https://github.com/linkerd/linkerd2/ + type: application + urls: + - assets/buoyant/linkerd-control-plane-2024.10.4.tgz + version: 2024.10.4 + - annotations: + catalog.cattle.io/auto-install: linkerd-crds + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Linkerd Control Plane + catalog.cattle.io/kube-version: '>=1.22.0-0' + catalog.cattle.io/release-name: linkerd-control-plane + apiVersion: v2 appVersion: edge-24.10.3 created: "2024-10-19T00:35:00.302033459Z" dependencies: @@ -24668,7 +24757,7 @@ entries: version: 0.1.0 description: 'Linkerd gives you observability, reliability, and security for your microservices — with no code change required. ' - digest: 525b51748732d64c2de24e2d002a9c7c3705229e3c03df309fe1f173add8c052 + digest: 4c3ea80138c0d21449585d336d569f4ecc9fe857a7611ec2ef804779d086546a home: https://linkerd.io icon: file://assets/icons/linkerd-control-plane.png keywords: @@ -25955,6 +26044,36 @@ entries: - assets/buoyant/linkerd-control-plane-1.12.5.tgz version: 1.12.5 linkerd-crds: + - annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Linkerd CRDs + catalog.cattle.io/kube-version: '>=1.22.0-0' + catalog.cattle.io/release-name: linkerd-crds + apiVersion: v2 + created: "2024-10-25T00:36:07.942709756Z" + dependencies: + - name: partials + repository: file://../partials + version: 0.1.0 + description: 'Linkerd gives you observability, reliability, and security for your + microservices — with no code change required. ' + digest: 15f979b60f4adb81db911c27b494c5544f0187e292603ba6d4ddf3864415c3b6 + home: https://linkerd.io + icon: file://assets/icons/linkerd-crds.png + keywords: + - service-mesh + kubeVersion: '>=1.22.0-0' + maintainers: + - email: cncf-linkerd-dev@lists.cncf.io + name: Linkerd authors + url: https://linkerd.io/ + name: linkerd-crds + sources: + - https://github.com/linkerd/linkerd2/ + type: application + urls: + - assets/buoyant/linkerd-crds-2024.10.4.tgz + version: 2024.10.4 - annotations: catalog.cattle.io/certified: partner catalog.cattle.io/display-name: Linkerd CRDs @@ -27559,6 +27678,54 @@ entries: - assets/loft/loft-3.2.0.tgz version: 3.2.0 microgateway: + - annotations: + artifacthub.io/category: security + artifacthub.io/license: MIT + artifacthub.io/links: | + - name: Airlock Microgateway Documentation + url: https://docs.airlock.com/microgateway/4.4/ + - name: Airlock Microgateway Labs + url: https://play.instruqt.com/airlock/invite/hyi9fy4b4jzc?icp_referrer=artifacthub.io + - name: Airlock Microgateway Forum + url: https://forum.airlock.com/ + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Airlock Microgateway + catalog.cattle.io/kube-version: '>=1.25.0-0' + catalog.cattle.io/release-name: microgateway + charts.openshift.io/name: Airlock Microgateway + apiVersion: v2 + appVersion: 4.4.0 + created: "2024-10-25T00:36:07.606664661Z" + description: A Helm chart for deploying the Airlock Microgateway + digest: a9d0d111ac45dbe36685f481819c9b45d8848494bb0cb7280db2aed901b3aaaa + home: https://www.airlock.com/en/microgateway + icon: file://assets/icons/microgateway.svg + keywords: + - WAF + - Web Application Firewall + - WAAP + - Web Application and API protection + - OWASP + - Airlock + - Microgateway + - Security + - Filtering + - DevSecOps + - shift left + - control plane + - Operator + kubeVersion: '>=1.25.0-0' + maintainers: + - email: support@airlock.com + name: Airlock + url: https://www.airlock.com/ + name: microgateway + sources: + - https://github.com/airlock/microgateway + type: application + urls: + - assets/airlock/microgateway-4.4.0.tgz + version: 4.4.0 - annotations: artifacthub.io/category: security artifacthub.io/license: MIT @@ -27848,6 +28015,53 @@ entries: - assets/airlock/microgateway-4.2.3.tgz version: 4.2.3 microgateway-cni: + - annotations: + artifacthub.io/category: security + artifacthub.io/license: MIT + artifacthub.io/links: | + - name: Airlock Microgateway Documentation + url: https://docs.airlock.com/microgateway/4.4/ + - name: Airlock Microgateway Labs + url: https://play.instruqt.com/airlock/invite/hyi9fy4b4jzc?icp_referrer=artifacthub.io + - name: Airlock Microgateway Forum + url: https://forum.airlock.com/ + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Airlock Microgateway CNI + catalog.cattle.io/kube-version: '>=1.25.0-0' + catalog.cattle.io/release-name: microgateway-cni + charts.openshift.io/name: Airlock Microgateway CNI + apiVersion: v2 + appVersion: 4.4.0 + created: "2024-10-25T00:36:07.612309656Z" + description: A Helm chart for deploying the Airlock Microgateway CNI plugin + digest: e571b2a90d31b40290e1b6f7b1a85c4f49b73668fd24a68aea518704a0c5f165 + home: https://www.airlock.com/en/microgateway + icon: file://assets/icons/microgateway-cni.svg + keywords: + - WAF + - Web Application Firewall + - WAAP + - Web Application and API protection + - OWASP + - Airlock + - Microgateway + - Security + - Filtering + - DevSecOps + - shift left + - CNI + kubeVersion: '>=1.25.0-0' + maintainers: + - email: support@airlock.com + name: Airlock + url: https://www.airlock.com/ + name: microgateway-cni + sources: + - https://github.com/airlock/microgateway + type: application + urls: + - assets/airlock/microgateway-cni-4.4.0.tgz + version: 4.4.0 - annotations: artifacthub.io/category: security artifacthub.io/license: MIT @@ -34724,6 +34938,31 @@ entries: - assets/percona/psmdb-operator-1.14.3.tgz version: 1.14.3 pxc-db: + - annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Percona XtraDB Cluster + catalog.cattle.io/kube-version: '>=1.21-0' + catalog.cattle.io/release-name: pxc-db + apiVersion: v2 + appVersion: 1.15.1 + created: "2024-10-25T00:36:12.021280728Z" + description: A Helm chart for installing Percona XtraDB Cluster Databases using + the PXC Operator. + digest: ee7067b1c46409e4370cec0c4b7a2e5f8067a2dd864daaab95ce393eaddab4f2 + home: https://www.percona.com/doc/kubernetes-operator-for-pxc/kubernetes.html + icon: file://assets/icons/pxc-db.png + kubeVersion: '>=1.21-0' + maintainers: + - email: tomislav.plavcic@percona.com + name: tplavcic + - email: sergey.pronin@percona.com + name: spron-in + - email: natalia.marukovich@percona.com + name: nmarukovich + name: pxc-db + urls: + - assets/percona/pxc-db-1.15.1.tgz + version: 1.15.1 - annotations: catalog.cattle.io/certified: partner catalog.cattle.io/display-name: Percona XtraDB Cluster @@ -35039,6 +35278,32 @@ entries: - assets/percona/pxc-db-1.12.3.tgz version: 1.12.3 pxc-operator: + - annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Percona Operator For MySQL based on Percona + XtraDB Cluster + catalog.cattle.io/kube-version: '>=1.21-0' + catalog.cattle.io/release-name: pxc-operator + apiVersion: v2 + appVersion: 1.15.1 + created: "2024-10-25T00:36:12.03371764Z" + description: A Helm chart for deploying the Percona Operator for MySQL (based + on Percona XtraDB Cluster) + digest: c25a354baa11c13d09d37560685d6db803832ab325c5b9f37a6823de0ace67fe + home: https://docs.percona.com/percona-operator-for-mysql/pxc/ + icon: file://assets/icons/pxc-operator.png + kubeVersion: '>=1.21-0' + maintainers: + - email: tomislav.plavcic@percona.com + name: tplavcic + - email: natalia.marukovich@percona.com + name: nmarukovich + - email: sergey.pronin@percona.com + name: spron-in + name: pxc-operator + urls: + - assets/percona/pxc-operator-1.15.1.tgz + version: 1.15.1 - annotations: catalog.cattle.io/certified: partner catalog.cattle.io/display-name: Percona Operator For MySQL based on Percona @@ -46055,4 +46320,4 @@ entries: urls: - assets/netfoundry/ziti-host-1.5.1.tgz version: 1.5.1 -generated: "2024-10-24T00:35:40.244604168Z" +generated: "2024-10-25T00:36:07.588047061Z"