From df2761b7be8c95dff5edbd7528e0ffc581517dc9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 17 Jan 2025 00:07:36 +0000 Subject: [PATCH] Added chart versions: buoyant/linkerd-control-plane: - 2025.1.1 buoyant/linkerd-crds: - 2025.1.1 dell/csi-isilon: - 2.13.0 dell/csi-powermax: - 2.13.0 dell/csi-powerstore: - 2.13.0 dell/csi-unity: - 2.13.0 dell/csi-vxflexos: - 2.13.0 --- .../linkerd-control-plane-2025.1.1.tgz | Bin 0 -> 33676 bytes assets/buoyant/linkerd-crds-2025.1.1.tgz | Bin 0 -> 126844 bytes assets/dell/csi-isilon-2.13.0.tgz | Bin 0 -> 10129 bytes assets/dell/csi-powermax-2.13.0.tgz | Bin 0 -> 14034 bytes assets/dell/csi-powerstore-2.13.0.tgz | Bin 0 -> 11584 bytes assets/dell/csi-unity-2.13.0.tgz | Bin 0 -> 9897 bytes assets/dell/csi-vxflexos-2.13.0.tgz | Bin 0 -> 9787 bytes .../2025.1.1/.helmignore | 22 + .../linkerd-control-plane/2025.1.1/Chart.lock | 6 + .../linkerd-control-plane/2025.1.1/Chart.yaml | 28 + .../linkerd-control-plane/2025.1.1/README.md | 325 + .../2025.1.1/README.md.gotmpl | 133 + .../2025.1.1/app-readme.md | 14 + .../2025.1.1/charts/partials/.helmignore | 21 + .../2025.1.1/charts/partials/Chart.yaml | 5 + .../2025.1.1/charts/partials/README.md | 9 + .../2025.1.1/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 | 101 + .../charts/partials/templates/_proxy.tpl | 277 + .../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 + .../2025.1.1/charts/partials/values.yaml | 0 .../2025.1.1/questions.yaml | 19 + .../2025.1.1/templates/NOTES.txt | 19 + .../2025.1.1/templates/config-rbac.yaml | 16 + .../2025.1.1/templates/config.yaml | 39 + .../2025.1.1/templates/destination-rbac.yaml | 339 ++ .../2025.1.1/templates/destination.yaml | 448 ++ .../2025.1.1/templates/heartbeat-rbac.yaml | 78 + .../2025.1.1/templates/heartbeat.yaml | 101 + .../2025.1.1/templates/identity-rbac.yaml | 49 + .../2025.1.1/templates/identity.yaml | 277 + .../2025.1.1/templates/namespace.yaml | 18 + .../2025.1.1/templates/podmonitor.yaml | 128 + .../templates/proxy-injector-rbac.yaml | 120 + .../2025.1.1/templates/proxy-injector.yaml | 227 + .../2025.1.1/templates/psp.yaml | 119 + .../2025.1.1/values-ha.yaml | 63 + .../2025.1.1/values.yaml | 680 +++ .../buoyant/linkerd-crds/2025.1.1/.helmignore | 22 + .../buoyant/linkerd-crds/2025.1.1/Chart.lock | 6 + .../buoyant/linkerd-crds/2025.1.1/Chart.yaml | 26 + .../buoyant/linkerd-crds/2025.1.1/README.md | 73 + .../linkerd-crds/2025.1.1/README.md.gotmpl | 59 + .../linkerd-crds/2025.1.1/app-readme.md | 9 + .../2025.1.1/charts/partials/.helmignore | 21 + .../2025.1.1/charts/partials/Chart.yaml | 5 + .../2025.1.1/charts/partials/README.md | 9 + .../2025.1.1/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 | 101 + .../charts/partials/templates/_proxy.tpl | 277 + .../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 + .../2025.1.1/charts/partials/values.yaml | 0 .../linkerd-crds/2025.1.1/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 + .../policy/http-local-ratelimit-policy.yaml | 215 + .../2025.1.1/templates/policy/httproute.yaml | 5328 +++++++++++++++++ .../policy/meshtls-authentication.yaml | 87 + .../policy/network-authentication.yaml | 53 + .../policy/server-authorization.yaml | 266 + .../2025.1.1/templates/policy/server.yaml | 319 + .../2025.1.1/templates/serviceprofile.yaml | 274 + .../templates/workload/external-workload.yaml | 303 + .../buoyant/linkerd-crds/2025.1.1/values.yaml | 3 + charts/dell/csi-isilon/2.13.0/Chart.yaml | 22 + charts/dell/csi-isilon/2.13.0/app-readme.md | 10 + .../csi-isilon/2.13.0/templates/_helpers.tpl | 10 + .../2.13.0/templates/controller.yaml | 533 ++ .../2.13.0/templates/csidriver.yaml | 14 + .../templates/driver-config-params.yaml | 14 + .../csi-isilon/2.13.0/templates/node.yaml | 348 ++ charts/dell/csi-isilon/2.13.0/values.yaml | 432 ++ charts/dell/csi-powermax/2.13.0/Chart.yaml | 28 + charts/dell/csi-powermax/2.13.0/app-readme.md | 16 + .../2.13.0/charts/csireverseproxy/Chart.yaml | 6 + .../charts/csireverseproxy/conf/config.yaml | 38 + .../csireverseproxy/templates/_helpers.tpl | 9 + .../templates/certificate.yaml | 72 + .../csireverseproxy/templates/configmap.yaml | 7 + .../templates/reverseproxy-rbac.yaml | 25 + .../templates/reverseproxy.yaml | 50 + .../csireverseproxy/templates/service.yaml | 17 + .../templates/serviceaccount.yaml | 7 + .../2.13.0/charts/csireverseproxy/values.yaml | 7 + .../2.13.0/templates/_helpers.tpl | 8 + .../2.13.0/templates/controller.yaml | 568 ++ .../2.13.0/templates/csidriver.yaml | 13 + .../templates/driver-config-params.yaml | 9 + .../csi-powermax/2.13.0/templates/node.yaml | 517 ++ .../templates/powermax-array-config.yaml | 14 + charts/dell/csi-powermax/2.13.0/values.yaml | 510 ++ charts/dell/csi-powerstore/2.13.0/Chart.yaml | 23 + .../dell/csi-powerstore/2.13.0/app-readme.md | 92 + .../2.13.0/templates/_helpers.tpl | 10 + .../2.13.0/templates/controller.yaml | 457 ++ .../2.13.0/templates/csidriver.yaml | 29 + .../templates/driver-config-params.yaml | 31 + .../csi-powerstore/2.13.0/templates/node.yaml | 353 ++ charts/dell/csi-powerstore/2.13.0/values.yaml | 363 ++ charts/dell/csi-unity/2.13.0/Chart.yaml | 22 + charts/dell/csi-unity/2.13.0/app-readme.md | 93 + .../csi-unity/2.13.0/templates/_helpers.tpl | 10 + .../2.13.0/templates/controller.yaml | 328 + .../csi-unity/2.13.0/templates/csidriver.yaml | 14 + .../templates/driver-config-params.yaml | 18 + .../dell/csi-unity/2.13.0/templates/node.yaml | 283 + charts/dell/csi-unity/2.13.0/values.yaml | 283 + charts/dell/csi-vxflexos/2.13.0/Chart.yaml | 22 + charts/dell/csi-vxflexos/2.13.0/app-readme.md | 10 + .../dell/csi-vxflexos/2.13.0/questions.yaml | 21 + .../2.13.0/templates/_helpers.tpl | 10 + .../2.13.0/templates/controller.yaml | 476 ++ .../2.13.0/templates/csidriver.yaml | 14 + .../templates/driver-config-params.yaml | 20 + .../csi-vxflexos/2.13.0/templates/node.yaml | 441 ++ charts/dell/csi-vxflexos/2.13.0/values.yaml | 378 ++ index.yaml | 201 +- 149 files changed, 25741 insertions(+), 1 deletion(-) create mode 100644 assets/buoyant/linkerd-control-plane-2025.1.1.tgz create mode 100644 assets/buoyant/linkerd-crds-2025.1.1.tgz create mode 100644 assets/dell/csi-isilon-2.13.0.tgz create mode 100644 assets/dell/csi-powermax-2.13.0.tgz create mode 100644 assets/dell/csi-powerstore-2.13.0.tgz create mode 100644 assets/dell/csi-unity-2.13.0.tgz create mode 100644 assets/dell/csi-vxflexos-2.13.0.tgz create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/.helmignore create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/Chart.lock create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/Chart.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/README.md create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/README.md.gotmpl create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/app-readme.md create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/.helmignore create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/Chart.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/README.md create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/README.md.gotmpl create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/NOTES.txt create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_affinity.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_capabilities.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_debug.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_helpers.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_metadata.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_network-validator.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_nodeselector.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_proxy-config-ann.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_proxy-init.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_proxy.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_pull-secrets.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_resources.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_tolerations.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_trace.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_validate.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_volumes.tpl create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/values.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/questions.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/templates/NOTES.txt create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/templates/config-rbac.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/templates/config.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/templates/destination-rbac.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/templates/destination.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/templates/heartbeat-rbac.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/templates/heartbeat.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/templates/identity-rbac.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/templates/identity.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/templates/namespace.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/templates/podmonitor.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/templates/proxy-injector-rbac.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/templates/proxy-injector.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/templates/psp.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/values-ha.yaml create mode 100644 charts/buoyant/linkerd-control-plane/2025.1.1/values.yaml create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/.helmignore create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/Chart.lock create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/Chart.yaml create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/README.md create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/README.md.gotmpl create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/app-readme.md create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/charts/partials/.helmignore create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/charts/partials/Chart.yaml create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/charts/partials/README.md create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/charts/partials/README.md.gotmpl create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/NOTES.txt create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_affinity.tpl create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_capabilities.tpl create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_debug.tpl create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_helpers.tpl create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_metadata.tpl create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_network-validator.tpl create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_nodeselector.tpl create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_proxy-config-ann.tpl create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_proxy-init.tpl create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_proxy.tpl create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_pull-secrets.tpl create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_resources.tpl create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_tolerations.tpl create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_trace.tpl create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_validate.tpl create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_volumes.tpl create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/charts/partials/values.yaml create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/templates/NOTES.txt create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/templates/gateway.networking.k8s.io_grpcroutes.yaml create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/templates/gateway.networking.k8s.io_httproutes.yaml create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/templates/gateway.networking.k8s.io_tcproutes.yaml create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/templates/gateway.networking.k8s.io_tlsroutes.yaml create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/templates/policy/authorization-policy.yaml create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/templates/policy/egress-network.yaml create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/templates/policy/http-local-ratelimit-policy.yaml create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/templates/policy/httproute.yaml create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/templates/policy/meshtls-authentication.yaml create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/templates/policy/network-authentication.yaml create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/templates/policy/server-authorization.yaml create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/templates/policy/server.yaml create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/templates/serviceprofile.yaml create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/templates/workload/external-workload.yaml create mode 100644 charts/buoyant/linkerd-crds/2025.1.1/values.yaml create mode 100644 charts/dell/csi-isilon/2.13.0/Chart.yaml create mode 100644 charts/dell/csi-isilon/2.13.0/app-readme.md create mode 100644 charts/dell/csi-isilon/2.13.0/templates/_helpers.tpl create mode 100644 charts/dell/csi-isilon/2.13.0/templates/controller.yaml create mode 100644 charts/dell/csi-isilon/2.13.0/templates/csidriver.yaml create mode 100644 charts/dell/csi-isilon/2.13.0/templates/driver-config-params.yaml create mode 100644 charts/dell/csi-isilon/2.13.0/templates/node.yaml create mode 100644 charts/dell/csi-isilon/2.13.0/values.yaml create mode 100644 charts/dell/csi-powermax/2.13.0/Chart.yaml create mode 100644 charts/dell/csi-powermax/2.13.0/app-readme.md create mode 100644 charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/Chart.yaml create mode 100644 charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/conf/config.yaml create mode 100644 charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/templates/_helpers.tpl create mode 100644 charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/templates/certificate.yaml create mode 100644 charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/templates/configmap.yaml create mode 100644 charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/templates/reverseproxy-rbac.yaml create mode 100644 charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/templates/reverseproxy.yaml create mode 100644 charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/templates/service.yaml create mode 100644 charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/templates/serviceaccount.yaml create mode 100644 charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/values.yaml create mode 100644 charts/dell/csi-powermax/2.13.0/templates/_helpers.tpl create mode 100644 charts/dell/csi-powermax/2.13.0/templates/controller.yaml create mode 100644 charts/dell/csi-powermax/2.13.0/templates/csidriver.yaml create mode 100644 charts/dell/csi-powermax/2.13.0/templates/driver-config-params.yaml create mode 100644 charts/dell/csi-powermax/2.13.0/templates/node.yaml create mode 100644 charts/dell/csi-powermax/2.13.0/templates/powermax-array-config.yaml create mode 100644 charts/dell/csi-powermax/2.13.0/values.yaml create mode 100644 charts/dell/csi-powerstore/2.13.0/Chart.yaml create mode 100644 charts/dell/csi-powerstore/2.13.0/app-readme.md create mode 100644 charts/dell/csi-powerstore/2.13.0/templates/_helpers.tpl create mode 100644 charts/dell/csi-powerstore/2.13.0/templates/controller.yaml create mode 100644 charts/dell/csi-powerstore/2.13.0/templates/csidriver.yaml create mode 100644 charts/dell/csi-powerstore/2.13.0/templates/driver-config-params.yaml create mode 100644 charts/dell/csi-powerstore/2.13.0/templates/node.yaml create mode 100644 charts/dell/csi-powerstore/2.13.0/values.yaml create mode 100644 charts/dell/csi-unity/2.13.0/Chart.yaml create mode 100644 charts/dell/csi-unity/2.13.0/app-readme.md create mode 100644 charts/dell/csi-unity/2.13.0/templates/_helpers.tpl create mode 100644 charts/dell/csi-unity/2.13.0/templates/controller.yaml create mode 100644 charts/dell/csi-unity/2.13.0/templates/csidriver.yaml create mode 100644 charts/dell/csi-unity/2.13.0/templates/driver-config-params.yaml create mode 100644 charts/dell/csi-unity/2.13.0/templates/node.yaml create mode 100644 charts/dell/csi-unity/2.13.0/values.yaml create mode 100644 charts/dell/csi-vxflexos/2.13.0/Chart.yaml create mode 100644 charts/dell/csi-vxflexos/2.13.0/app-readme.md create mode 100644 charts/dell/csi-vxflexos/2.13.0/questions.yaml create mode 100644 charts/dell/csi-vxflexos/2.13.0/templates/_helpers.tpl create mode 100644 charts/dell/csi-vxflexos/2.13.0/templates/controller.yaml create mode 100644 charts/dell/csi-vxflexos/2.13.0/templates/csidriver.yaml create mode 100644 charts/dell/csi-vxflexos/2.13.0/templates/driver-config-params.yaml create mode 100644 charts/dell/csi-vxflexos/2.13.0/templates/node.yaml create mode 100644 charts/dell/csi-vxflexos/2.13.0/values.yaml diff --git a/assets/buoyant/linkerd-control-plane-2025.1.1.tgz b/assets/buoyant/linkerd-control-plane-2025.1.1.tgz new file mode 100644 index 0000000000000000000000000000000000000000..4797cf2687c6b4763bfdefc7dde1e31eb04dec3d GIT binary patch literal 33676 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 z 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 E0IISjVE_OC literal 0 HcmV?d00001 diff --git a/assets/dell/csi-isilon-2.13.0.tgz b/assets/dell/csi-isilon-2.13.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..b8233c028bada3bfc983049a2b20619df40c43a7 GIT binary patch literal 10129 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⋙@?Zfc;{Y?*m-i~_XoBm|d z9}Xs!#&lsO=L#+8|1cPid$;cgM*YJ1ym!1oQjNofG=I4x>r?a>y)oH6 z-{rZ#0VyWI7*1kq^1-^rv~@IF9*xHrzuaC92ZP?_wEv+${pEHvyuQ92Ure_Mt7jtg zs6_R1i|cvwwzI)EROt1y?S0wXL%qq1dv8CF@=ey|9Brq)zfW&R8Yl z+mWdY>?>1uG^#eq*&2^t&;=#^dQf+lAF}W4@M;I`n_T^|cX2)a@%Cmo=ud~^>T+u* z>cdQwJb7O-Y-cjL290u?H#^~So9nt-ag57)`Fh>TSOJg^a84K7)a8r6>y`Stf77q9 z3i$XzwfUOl_1Vwm1HZwgoWpy;cTTW747_!m^m!9(mTg<$Zz=fLZu%7i&@&bG2t&^{ zqCE8c7#46POFftHUGJ|}a8}K3lEM<_xi3|^KFOxcBHQ1> z99_Tvp+C6oU-bqGf3HLYC97v46l3g6hd=cSX)YF_I=j|SWL+Xg<3s+3HH@}@HYB?v zL4LNVM~7X54OK|sxc6?{oBa5tDdc3%iCFF>8@F6dw?%9a>9mS!ZWH^u9s2r&24_q* zQuiAif-4W>XhN|JV@k&D@}D<}rn(#At#Bd+~oQCIj2 z%q4`VBz|Eof;h*4g3$Q*u@nOE0(vW3vOsIyxzZN3Q@YH+sA=`Wpe`RjU$^M4C!4lIt5{(EQs|JScx2Yxx}>pT8y*_1(fIhnp@0}5o50KvtuRIZyWB!O7cxl~wG zbrLR#PdN6hqhH9`oO(J)S8}S>$Xb#aNC%RQjn1F4$3o`zgE<=pH#%WZ{2_k>5jvU= z9e|$;$UatJzhu6|*?yss*K|&twd0W+=33>Vfktr}O7o&;dGk1et8o68p7$MwW7qtD z^CEZu$65RAW#?)Be~zax|No`cYpUnM%V;%K(&1UCrDLs&{smLua?-!O9{zB9-TTnH z&RVn{TU?VB@nVxTn-pr}Bm>Hr?fA>#U^*UNU-!n9p%>SDHyqzwOgE~&gZr@Zbz%2# zPkeEmYMA!ZbPDG`T*!Eor5~Q{^Z!Ms{rXM*{-4gPr~7|C%X7T@Kh&W4nr{98diJ^V zV`paR12XI$n0#71=Diffcbn{=M|bZlP7b{1;7E5sd|FQUul$0GPiY*g7i#9#k<4O; z84Sh2Y1ShPr4(;cn1D06q#PCq$uU*5qAanf9tomf9{GxnPe|)r`9hViAme*++TPEZ zKN`gBCufc}i^n79j5b*I%odG_&w>g0lw{F+td0MfX`&5-zQzQ_K$>CncFRINX5TEe z6JjzX8p)pD)ZRDWaXeA-#t!K0pOlVw6T$(Bcmc>Kq~P7CUx|h#6LLW0J>z$o<9Rm_ z1)DN}BmQ(y3g6qqQutQQynDR7yLK%$&Q95l^<*J0aB6T;7hvX(cd+ZKMQifQnU0CSD!4|bZ*3G6tJ^_u`txx7Q-vrV z7^uWGro^u3og>G@eG%R2k;Czh8x!`}p;C_&K=>t<*AxZ)(ed%a4Tnu-e&?VcIb`9{ z;x9jUkmTdUUg%)S$?d2&hQ8iSzK3?YGS90sgf(ps;F%wQH!IS{5a{Y-Z z`Z+RA6{m+v=b?g;zthHQH0(~7C0`$qVYxg%f=O4!xcpi-iFeD%49m8MZl;SKE^~M1 zczX(admk1c zC#VhDidTnEC#y_7uS(Ze_5BFs?W4&$IV;iNhEkHe>%NOrj&R>Y{j#}yyekZ%hcUpMs6OJ@5Gls zH!nl+6h!?W@TB%6a2Pf?!LCo5zI#j@JK{e(ug-GupPd)4-@JH=|N0!y$q5>90vic} zN+Q0keOv1^k=6(iPF&XEFW0R0=-b*^s*qk4Oo_T(snt?mM>K%_hy;$k8V1kUiZsy# z`=LkBuNnsUtNiwCDP<_St=59dWi)F#%x}5G^FRqt4DHs;W3!fzY0we?MYJ@ORtnhj z=C>ff{-*UZztbLP2(BP z9B_exNF?C`xEhm+16Tc`JkANAp?&+bI6gg3&(rg7@%;Y)009607Vd1N0QLX?t;nIJ literal 0 HcmV?d00001 diff --git a/assets/dell/csi-powermax-2.13.0.tgz b/assets/dell/csi-powermax-2.13.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..5e234e07b250cb65ebf84ed3fc28aaf461a7c860 GIT binary patch literal 14034 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 E0ESpXu>b%7 literal 0 HcmV?d00001 diff --git a/assets/dell/csi-powerstore-2.13.0.tgz b/assets/dell/csi-powerstore-2.13.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..28fef9afde6d2e4b13f604b0f7a3f576c3e5cc00 GIT binary patch literal 11584 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 literal 0 HcmV?d00001 diff --git a/assets/dell/csi-unity-2.13.0.tgz b/assets/dell/csi-unity-2.13.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..644b7846c556c3b2a32d2aab19db8ac55ed03a10 GIT binary patch literal 9897 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 bzCK@{ug}-#j?e!K00960uy8O#0LB0S|7nZu literal 0 HcmV?d00001 diff --git a/assets/dell/csi-vxflexos-2.13.0.tgz b/assets/dell/csi-vxflexos-2.13.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..0c0ab0747cae93626a32e978a7d81948fb9cb1e7 GIT binary patch literal 9787 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.22.0-0' + catalog.cattle.io/release-name: linkerd-control-plane +apiVersion: v2 +appVersion: edge-25.1.1 +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: 2025.1.1 diff --git a/charts/buoyant/linkerd-control-plane/2025.1.1/README.md b/charts/buoyant/linkerd-control-plane/2025.1.1/README.md new file mode 100644 index 0000000000..59b78c4690 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/README.md @@ -0,0 +1,325 @@ +# linkerd-control-plane + +Linkerd gives you observability, reliability, and security +for your microservices — with no code change required. + +![Version: 2025.1.1](https://img.shields.io/badge/Version-2025.1.1-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.podAnnotations | object | `{}` | Additional annotations to add to destination pods | +| 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) | +| egress.globalEgressNetworkNamespace | string | `"linkerd-egress"` | The namespace that is used to store egress configuration that affects all client workloads in the cluster | +| 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.podAnnotations | object | `{}` | Additional annotations to add to identity pods | +| 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` | Changes the default value for the nf_conntrack_tcp_timeout_close_wait kernel parameter. If used, runAsRoot needs to be true. | +| 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.2"` | 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.podAnnotations | object | `{}` | Additional annotations to add to proxy-injector pods | +| 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.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2) diff --git a/charts/buoyant/linkerd-control-plane/2025.1.1/README.md.gotmpl b/charts/buoyant/linkerd-control-plane/2025.1.1/README.md.gotmpl new file mode 100644 index 0000000000..19da2a82d6 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/app-readme.md b/charts/buoyant/linkerd-control-plane/2025.1.1/app-readme.md new file mode 100644 index 0000000000..351eac5f0d --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/charts/partials/.helmignore b/charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/.helmignore new file mode 100644 index 0000000000..f0c1319444 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/charts/partials/Chart.yaml b/charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/Chart.yaml new file mode 100644 index 0000000000..23cfc167e3 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/charts/partials/README.md b/charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/README.md new file mode 100644 index 0000000000..ee32c55174 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2) diff --git a/charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/README.md.gotmpl b/charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/README.md.gotmpl new file mode 100644 index 0000000000..37f5101061 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/charts/partials/templates/NOTES.txt b/charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/NOTES.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_affinity.tpl b/charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_affinity.tpl new file mode 100644 index 0000000000..5dde1da473 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/charts/partials/templates/_capabilities.tpl b/charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_capabilities.tpl new file mode 100644 index 0000000000..a595d74c1f --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/charts/partials/templates/_debug.tpl b/charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_debug.tpl new file mode 100644 index 0000000000..4df8cc77bc --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/charts/partials/templates/_helpers.tpl b/charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_helpers.tpl new file mode 100644 index 0000000000..b6cdc34d08 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/charts/partials/templates/_metadata.tpl b/charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_metadata.tpl new file mode 100644 index 0000000000..04d2f1beab --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/charts/partials/templates/_network-validator.tpl b/charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_network-validator.tpl new file mode 100644 index 0000000000..276056395f --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/charts/partials/templates/_nodeselector.tpl b/charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_nodeselector.tpl new file mode 100644 index 0000000000..4cde0ab16e --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/charts/partials/templates/_proxy-config-ann.tpl b/charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_proxy-config-ann.tpl new file mode 100644 index 0000000000..9651b3bd1a --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/charts/partials/templates/_proxy-init.tpl b/charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_proxy-init.tpl new file mode 100644 index 0000000000..5e513dd220 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_proxy-init.tpl @@ -0,0 +1,101 @@ +{{- 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 }} + {{- if not .Values.proxyInit.runAsRoot }} +{{ fail "proxyInit.runAsRoot must be set to use proxyInit.closeWaitTimeoutSecs" }} + {{- end }} +- --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/2025.1.1/charts/partials/templates/_proxy.tpl b/charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_proxy.tpl new file mode 100644 index 0000000000..53cedb3973 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_proxy.tpl @@ -0,0 +1,277 @@ +{{ 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_uid + valueFrom: + fieldRef: + fieldPath: metadata.uid +- name: _pod_nodeName + valueFrom: + fieldRef: + fieldPath: spec.nodeName +- name: _pod_containerName + value: &containerName linkerd-proxy +{{- 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: {{ printf "%s%s" .Values.proxy.logLevel (.Values.proxy.logHTTPHeaders | eq "insecure" | ternary "" ",[{headers}]=off,[{request}]=off") | quote }} +- 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: *containerName +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/2025.1.1/charts/partials/templates/_pull-secrets.tpl b/charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_pull-secrets.tpl new file mode 100644 index 0000000000..0c9aa4f01c --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/charts/partials/templates/_resources.tpl b/charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_resources.tpl new file mode 100644 index 0000000000..1fd6789fd7 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/charts/partials/templates/_tolerations.tpl b/charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_tolerations.tpl new file mode 100644 index 0000000000..c2292b1464 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/charts/partials/templates/_trace.tpl b/charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_trace.tpl new file mode 100644 index 0000000000..dee059541f --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/charts/partials/templates/_validate.tpl b/charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_validate.tpl new file mode 100644 index 0000000000..ba772c2fee --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/charts/partials/templates/_volumes.tpl b/charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/templates/_volumes.tpl new file mode 100644 index 0000000000..ecb24cfe63 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/charts/partials/values.yaml b/charts/buoyant/linkerd-control-plane/2025.1.1/charts/partials/values.yaml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/charts/buoyant/linkerd-control-plane/2025.1.1/questions.yaml b/charts/buoyant/linkerd-control-plane/2025.1.1/questions.yaml new file mode 100644 index 0000000000..4ae27870a3 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/templates/NOTES.txt b/charts/buoyant/linkerd-control-plane/2025.1.1/templates/NOTES.txt new file mode 100644 index 0000000000..4bd1be9fc0 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/templates/config-rbac.yaml b/charts/buoyant/linkerd-control-plane/2025.1.1/templates/config-rbac.yaml new file mode 100644 index 0000000000..5f5c34203e --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/templates/config.yaml b/charts/buoyant/linkerd-control-plane/2025.1.1/templates/config.yaml new file mode 100644 index 0000000000..a9cea5f421 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/templates/destination-rbac.yaml b/charts/buoyant/linkerd-control-plane/2025.1.1/templates/destination-rbac.yaml new file mode 100644 index 0000000000..83f96bca02 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/templates/destination-rbac.yaml @@ -0,0 +1,339 @@ +--- +### +### 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 + - httplocalratelimitpolicies + - 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 + - httplocalratelimitpolicies + - 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 + - httplocalratelimitpolicies/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/2025.1.1/templates/destination.yaml b/charts/buoyant/linkerd-control-plane/2025.1.1/templates/destination.yaml new file mode 100644 index 0000000000..45e7813530 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/templates/destination.yaml @@ -0,0 +1,448 @@ +--- +### +### 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 (mergeOverwrite (deepCopy .Values.podAnnotations) .Values.destinationController.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}} + - --global-egress-network-namespace={{.Values.egress.globalEgressNetworkNamespace}} + {{- 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 + initialDelaySeconds: 10 + {{- 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 + {{- 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/2025.1.1/templates/heartbeat-rbac.yaml b/charts/buoyant/linkerd-control-plane/2025.1.1/templates/heartbeat-rbac.yaml new file mode 100644 index 0000000000..7b127543f4 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/templates/heartbeat.yaml b/charts/buoyant/linkerd-control-plane/2025.1.1/templates/heartbeat.yaml new file mode 100644 index 0000000000..1f42493849 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/templates/identity-rbac.yaml b/charts/buoyant/linkerd-control-plane/2025.1.1/templates/identity-rbac.yaml new file mode 100644 index 0000000000..6efdb4e104 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/templates/identity.yaml b/charts/buoyant/linkerd-control-plane/2025.1.1/templates/identity.yaml new file mode 100644 index 0000000000..42e08bc109 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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 (mergeOverwrite (deepCopy .Values.podAnnotations) .Values.identity.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/2025.1.1/templates/namespace.yaml b/charts/buoyant/linkerd-control-plane/2025.1.1/templates/namespace.yaml new file mode 100644 index 0000000000..61461c1327 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/templates/podmonitor.yaml b/charts/buoyant/linkerd-control-plane/2025.1.1/templates/podmonitor.yaml new file mode 100644 index 0000000000..fd2b5d6ceb --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/templates/proxy-injector-rbac.yaml b/charts/buoyant/linkerd-control-plane/2025.1.1/templates/proxy-injector-rbac.yaml new file mode 100644 index 0000000000..c2c84c5c17 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/templates/proxy-injector.yaml b/charts/buoyant/linkerd-control-plane/2025.1.1/templates/proxy-injector.yaml new file mode 100644 index 0000000000..9a947458fb --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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 (mergeOverwrite (deepCopy .Values.podAnnotations) .Values.identity.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/2025.1.1/templates/psp.yaml b/charts/buoyant/linkerd-control-plane/2025.1.1/templates/psp.yaml new file mode 100644 index 0000000000..db91fea675 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/values-ha.yaml b/charts/buoyant/linkerd-control-plane/2025.1.1/values-ha.yaml new file mode 100644 index 0000000000..e3b8cbc070 --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/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/2025.1.1/values.yaml b/charts/buoyant/linkerd-control-plane/2025.1.1/values.yaml new file mode 100644 index 0000000000..71e5ef278e --- /dev/null +++ b/charts/buoyant/linkerd-control-plane/2025.1.1/values.yaml @@ -0,0 +1,680 @@ +# 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-25.1.1 +# -- 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.2 + # -- Changes the default value for the nf_conntrack_tcp_timeout_close_wait + # kernel parameter. If used, runAsRoot needs to be true. + 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 + # -- Additional annotations to add to destination pods + podAnnotations: {} + 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 + + # -- Additional annotations to add to identity pods + podAnnotations: {} + + 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: "" + + # -- Additional annotations to add to proxy-injector pods + podAnnotations: {} + + 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 + + +# Egress related configuration +egress: + # -- The namespace that is used to store egress configuration that affects all client workloads in the cluster + globalEgressNetworkNamespace: linkerd-egress diff --git a/charts/buoyant/linkerd-crds/2025.1.1/.helmignore b/charts/buoyant/linkerd-crds/2025.1.1/.helmignore new file mode 100644 index 0000000000..79c90a8063 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.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 +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/2025.1.1/Chart.lock b/charts/buoyant/linkerd-crds/2025.1.1/Chart.lock new file mode 100644 index 0000000000..a62a030631 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/Chart.yaml b/charts/buoyant/linkerd-crds/2025.1.1/Chart.yaml new file mode 100644 index 0000000000..79ec33b1df --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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: 2025.1.1 diff --git a/charts/buoyant/linkerd-crds/2025.1.1/README.md b/charts/buoyant/linkerd-crds/2025.1.1/README.md new file mode 100644 index 0000000000..699a1d5616 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/README.md @@ -0,0 +1,73 @@ +# linkerd-crds + +Linkerd gives you observability, reliability, and security +for your microservices — with no code change required. + +![Version: 2025.1.1](https://img.shields.io/badge/Version-2025.1.1-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.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2) diff --git a/charts/buoyant/linkerd-crds/2025.1.1/README.md.gotmpl b/charts/buoyant/linkerd-crds/2025.1.1/README.md.gotmpl new file mode 100644 index 0000000000..88be739549 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/app-readme.md b/charts/buoyant/linkerd-crds/2025.1.1/app-readme.md new file mode 100644 index 0000000000..59010a6b21 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/charts/partials/.helmignore b/charts/buoyant/linkerd-crds/2025.1.1/charts/partials/.helmignore new file mode 100644 index 0000000000..f0c1319444 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/charts/partials/Chart.yaml b/charts/buoyant/linkerd-crds/2025.1.1/charts/partials/Chart.yaml new file mode 100644 index 0000000000..23cfc167e3 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/charts/partials/README.md b/charts/buoyant/linkerd-crds/2025.1.1/charts/partials/README.md new file mode 100644 index 0000000000..ee32c55174 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2) diff --git a/charts/buoyant/linkerd-crds/2025.1.1/charts/partials/README.md.gotmpl b/charts/buoyant/linkerd-crds/2025.1.1/charts/partials/README.md.gotmpl new file mode 100644 index 0000000000..37f5101061 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/charts/partials/templates/NOTES.txt b/charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/NOTES.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_affinity.tpl b/charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_affinity.tpl new file mode 100644 index 0000000000..5dde1da473 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/charts/partials/templates/_capabilities.tpl b/charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_capabilities.tpl new file mode 100644 index 0000000000..a595d74c1f --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/charts/partials/templates/_debug.tpl b/charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_debug.tpl new file mode 100644 index 0000000000..4df8cc77bc --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/charts/partials/templates/_helpers.tpl b/charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_helpers.tpl new file mode 100644 index 0000000000..b6cdc34d08 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/charts/partials/templates/_metadata.tpl b/charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_metadata.tpl new file mode 100644 index 0000000000..04d2f1beab --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/charts/partials/templates/_network-validator.tpl b/charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_network-validator.tpl new file mode 100644 index 0000000000..276056395f --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/charts/partials/templates/_nodeselector.tpl b/charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_nodeselector.tpl new file mode 100644 index 0000000000..4cde0ab16e --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/charts/partials/templates/_proxy-config-ann.tpl b/charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_proxy-config-ann.tpl new file mode 100644 index 0000000000..9651b3bd1a --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/charts/partials/templates/_proxy-init.tpl b/charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_proxy-init.tpl new file mode 100644 index 0000000000..5e513dd220 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_proxy-init.tpl @@ -0,0 +1,101 @@ +{{- 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 }} + {{- if not .Values.proxyInit.runAsRoot }} +{{ fail "proxyInit.runAsRoot must be set to use proxyInit.closeWaitTimeoutSecs" }} + {{- end }} +- --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/2025.1.1/charts/partials/templates/_proxy.tpl b/charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_proxy.tpl new file mode 100644 index 0000000000..53cedb3973 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_proxy.tpl @@ -0,0 +1,277 @@ +{{ 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_uid + valueFrom: + fieldRef: + fieldPath: metadata.uid +- name: _pod_nodeName + valueFrom: + fieldRef: + fieldPath: spec.nodeName +- name: _pod_containerName + value: &containerName linkerd-proxy +{{- 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: {{ printf "%s%s" .Values.proxy.logLevel (.Values.proxy.logHTTPHeaders | eq "insecure" | ternary "" ",[{headers}]=off,[{request}]=off") | quote }} +- 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: *containerName +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/2025.1.1/charts/partials/templates/_pull-secrets.tpl b/charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_pull-secrets.tpl new file mode 100644 index 0000000000..0c9aa4f01c --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/charts/partials/templates/_resources.tpl b/charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_resources.tpl new file mode 100644 index 0000000000..1fd6789fd7 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/charts/partials/templates/_tolerations.tpl b/charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_tolerations.tpl new file mode 100644 index 0000000000..c2292b1464 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/charts/partials/templates/_trace.tpl b/charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_trace.tpl new file mode 100644 index 0000000000..dee059541f --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/charts/partials/templates/_validate.tpl b/charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_validate.tpl new file mode 100644 index 0000000000..ba772c2fee --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/charts/partials/templates/_volumes.tpl b/charts/buoyant/linkerd-crds/2025.1.1/charts/partials/templates/_volumes.tpl new file mode 100644 index 0000000000..ecb24cfe63 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/charts/partials/values.yaml b/charts/buoyant/linkerd-crds/2025.1.1/charts/partials/values.yaml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/charts/buoyant/linkerd-crds/2025.1.1/templates/NOTES.txt b/charts/buoyant/linkerd-crds/2025.1.1/templates/NOTES.txt new file mode 100644 index 0000000000..4ff5c1818a --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/templates/gateway.networking.k8s.io_grpcroutes.yaml b/charts/buoyant/linkerd-crds/2025.1.1/templates/gateway.networking.k8s.io_grpcroutes.yaml new file mode 100644 index 0000000000..0050aac88b --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/templates/gateway.networking.k8s.io_httproutes.yaml b/charts/buoyant/linkerd-crds/2025.1.1/templates/gateway.networking.k8s.io_httproutes.yaml new file mode 100644 index 0000000000..b695c51d50 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/templates/gateway.networking.k8s.io_tcproutes.yaml b/charts/buoyant/linkerd-crds/2025.1.1/templates/gateway.networking.k8s.io_tcproutes.yaml new file mode 100644 index 0000000000..cb17293b73 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/templates/gateway.networking.k8s.io_tlsroutes.yaml b/charts/buoyant/linkerd-crds/2025.1.1/templates/gateway.networking.k8s.io_tlsroutes.yaml new file mode 100644 index 0000000000..d79a19aaef --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/templates/policy/authorization-policy.yaml b/charts/buoyant/linkerd-crds/2025.1.1/templates/policy/authorization-policy.yaml new file mode 100644 index 0000000000..7d86520e2e --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/templates/policy/egress-network.yaml b/charts/buoyant/linkerd-crds/2025.1.1/templates/policy/egress-network.yaml new file mode 100644 index 0000000000..4289de0577 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/templates/policy/http-local-ratelimit-policy.yaml b/charts/buoyant/linkerd-crds/2025.1.1/templates/policy/http-local-ratelimit-policy.yaml new file mode 100644 index 0000000000..b539549459 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/templates/policy/http-local-ratelimit-policy.yaml @@ -0,0 +1,215 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: httplocalratelimitpolicies.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: HTTPLocalRateLimitPolicy + listKind: HTTPLocalRateLimitPolicyList + plural: httplocalratelimitpolicies + singular: httplocalratelimitpolicy + scope: Namespaced + versions: + - name: v1alpha1 + served: true + storage: true + subresources: + status: {} + schema: + openAPIV3Schema: + type: object + required: [spec] + properties: + spec: + type: object + required: [targetRef] + properties: + targetRef: + description: >- + TargetRef references a resource to which the rate limit + policy applies. Only Server is allowed. + 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 + total: + description: >- + Overall rate-limit, which all traffic coming to this + target should abide. + If unset no overall limit is applied. + type: object + required: [requestsPerSecond] + properties: + requestsPerSecond: + format: int64 + type: integer + identity: + description: >- + Fairness for individual identities; each separate client, + grouped by identity, will have this rate-limit. The + requestsPerSecond value should be less than or equal to the + total requestsPerSecond (if set). + type: object + required: [requestsPerSecond] + properties: + requestsPerSecond: + format: int64 + type: integer + overrides: + description: >- + Overrides for traffic from a specific client. The + requestsPerSecond value should be less than or equal to the + total requestsPerSecond (if set). + type: array + items: + type: object + required: [requestsPerSecond, clientRefs] + properties: + requestsPerSecond: + format: int64 + type: integer + clientRefs: + 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 + namespace: + description: >- + Namespace is the namespace of the referent. + When unspecified (or empty string), this refers to the + local namespace of the Policy. + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + 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 + targetRef: + properties: + group: + default: policy.linkerd.io + 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: Server + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + maxLength: 253 + minLength: 1 + type: string + required: + - name + type: object + required: + - targetRef + additionalPrinterColumns: + - name: Target_kind + description: The resource kind to which the rate-limit applies + type: string + jsonPath: .spec.targetRef.kind + - name: Target_name + type: string + description: The resource name to which the rate-limit applies + jsonPath: .spec.targetRef.name + - name: Total_RPS + description: The overall rate-limit + type: integer + format: int32 + jsonPath: .spec.total.requestsPerSecond + - name: Identity_RPS + description: The rate-limit per identity + type: integer + format: int32 + jsonPath: .spec.identity.requestsPerSecond diff --git a/charts/buoyant/linkerd-crds/2025.1.1/templates/policy/httproute.yaml b/charts/buoyant/linkerd-crds/2025.1.1/templates/policy/httproute.yaml new file mode 100644 index 0000000000..6d2e8b07ef --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/templates/policy/meshtls-authentication.yaml b/charts/buoyant/linkerd-crds/2025.1.1/templates/policy/meshtls-authentication.yaml new file mode 100644 index 0000000000..58ee815f59 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/templates/policy/network-authentication.yaml b/charts/buoyant/linkerd-crds/2025.1.1/templates/policy/network-authentication.yaml new file mode 100644 index 0000000000..cef15d3c40 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/templates/policy/server-authorization.yaml b/charts/buoyant/linkerd-crds/2025.1.1/templates/policy/server-authorization.yaml new file mode 100644 index 0000000000..33fb659002 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/templates/policy/server.yaml b/charts/buoyant/linkerd-crds/2025.1.1/templates/policy/server.yaml new file mode 100644 index 0000000000..3de5a34e0e --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/templates/serviceprofile.yaml b/charts/buoyant/linkerd-crds/2025.1.1/templates/serviceprofile.yaml new file mode 100644 index 0000000000..ad12c96a3a --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/templates/workload/external-workload.yaml b/charts/buoyant/linkerd-crds/2025.1.1/templates/workload/external-workload.yaml new file mode 100644 index 0000000000..2e6e43ae60 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/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/2025.1.1/values.yaml b/charts/buoyant/linkerd-crds/2025.1.1/values.yaml new file mode 100644 index 0000000000..2cc17719e3 --- /dev/null +++ b/charts/buoyant/linkerd-crds/2025.1.1/values.yaml @@ -0,0 +1,3 @@ +enableHttpRoutes: true +enableTlsRoutes: true +enableTcpRoutes: true diff --git a/charts/dell/csi-isilon/2.13.0/Chart.yaml b/charts/dell/csi-isilon/2.13.0/Chart.yaml new file mode 100644 index 0000000000..b36f51a891 --- /dev/null +++ b/charts/dell/csi-isilon/2.13.0/Chart.yaml @@ -0,0 +1,22 @@ +annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Dell CSI PowerScale + catalog.cattle.io/kube-version: '>= 1.21.0' + catalog.cattle.io/release-name: isilon +apiVersion: v2 +appVersion: 2.13.0 +description: 'PowerScale CSI (Container Storage Interface) driver Kubernetes integration. + This chart includes everything required to provision via CSI as well as an Isilon + StorageClass. ' +icon: file://assets/icons/csi-isilon.png +keywords: +- csi +- storage +kubeVersion: '>= 1.21.0' +maintainers: +- name: DellEMC +name: csi-isilon +sources: +- https://github.com/dell/csi-isilon +type: application +version: 2.13.0 diff --git a/charts/dell/csi-isilon/2.13.0/app-readme.md b/charts/dell/csi-isilon/2.13.0/app-readme.md new file mode 100644 index 0000000000..9a2d8781a5 --- /dev/null +++ b/charts/dell/csi-isilon/2.13.0/app-readme.md @@ -0,0 +1,10 @@ +## Prerequisites + +1. Create a namespace named isilon +2. Create a secret named "isilon-creds" in the namespace created above. Sample [secret.yaml](https://github.com/dell/csi-powerscale/blob/main/samples/secret/secret.yaml). + >Secret must be of type opaque. +3. Create a secret named "Isilon-cert-0" in the namespace created above. Sample [empty-secret.yaml](https://github.com/dell/csi-powerscale/blob/main/samples/secret/empty-secret.yaml). + >Secret must be of type opaque. +4. Create storage classes using ones from [samples](https://github.com/dell/csi-powerscale/blob/main/samples/storageclass) folder as an example. +5. Install the chart with the name "csi-islon". +The table [here](https://github.com/dell/csi-powerscale/blob/main/helm/csi-isilon/values.yaml) lists the configurable parameters of the chart and their default values diff --git a/charts/dell/csi-isilon/2.13.0/templates/_helpers.tpl b/charts/dell/csi-isilon/2.13.0/templates/_helpers.tpl new file mode 100644 index 0000000000..ecfe630d73 --- /dev/null +++ b/charts/dell/csi-isilon/2.13.0/templates/_helpers.tpl @@ -0,0 +1,10 @@ +{{/* +Return true if storage capacity tracking is enabled and is supported based on k8s version +*/}} +{{- define "csi-isilon.isStorageCapacitySupported" -}} +{{- if eq .Values.storageCapacity.enabled true -}} + {{- if and (eq .Capabilities.KubeVersion.Major "1") (ge (trimSuffix "+" .Capabilities.KubeVersion.Minor) "24") -}} + {{- true -}} + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/dell/csi-isilon/2.13.0/templates/controller.yaml b/charts/dell/csi-isilon/2.13.0/templates/controller.yaml new file mode 100644 index 0000000000..10944b2623 --- /dev/null +++ b/charts/dell/csi-isilon/2.13.0/templates/controller.yaml @@ -0,0 +1,533 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Release.Name }}-controller + namespace: {{ .Release.Namespace }} +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ .Release.Name }}-controller +rules: + - apiGroups: [""] + resources: ["events"] + verbs: ["list", "watch", "create", "update", "patch"] + - apiGroups: [""] + resources: ["nodes"] + {{- if hasKey .Values "podmon" }} + {{- if eq .Values.podmon.enabled true }} + verbs: ["get", "list", "watch", "patch"] + {{- else }} + verbs: ["get", "list", "watch"] + {{- end }} + {{- end }} + - apiGroups: [""] + resources: ["persistentvolumes"] + verbs: ["get", "list", "watch", "create", "delete", "update"] + - apiGroups: [""] + resources: ["persistentvolumeclaims"] + verbs: ["get", "list", "watch", "update"] + - apiGroups: [""] + resources: ["pods"] + {{- if hasKey .Values "podmon" }} + {{- if eq .Values.podmon.enabled true }} + verbs: ["get", "list", "watch", "update", "delete"] + {{- else }} + verbs: ["get", "list", "watch"] + {{- end }} + {{- end }} + - apiGroups: ["storage.k8s.io"] + resources: ["storageclasses"] + verbs: ["get", "list", "watch"] + - apiGroups: ["storage.k8s.io"] + resources: ["volumeattachments"] + {{- if hasKey .Values "podmon" }} + {{- if eq .Values.podmon.enabled true }} + verbs: ["get", "list", "watch", "update", "patch", "delete"] + {{- else }} + verbs: ["get", "list", "watch", "update", "patch"] + {{- end }} + {{- end }} + - apiGroups: ["storage.k8s.io"] + resources: ["volumeattachments/status"] + verbs: ["patch"] + - apiGroups: ["csi.storage.k8s.io"] + resources: ["csinodeinfos"] + verbs: ["get", "list", "watch"] + - apiGroups: ["storage.k8s.io"] + resources: ["csinodes"] + verbs: ["get", "list", "watch", "update"] +# below for snapshotter + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "list"] + - apiGroups: ["snapshot.storage.k8s.io"] + resources: ["volumesnapshotclasses"] + verbs: ["get", "list", "watch"] + - apiGroups: ["snapshot.storage.k8s.io"] + resources: ["volumesnapshotcontents"] + verbs: ["create", "get", "list", "watch", "update", "delete", "patch"] + - apiGroups: ["snapshot.storage.k8s.io"] + resources: ["volumesnapshots"] + verbs: ["get", "list", "watch", "update"] + - apiGroups: ["snapshot.storage.k8s.io"] + resources: ["volumesnapshots/status"] + verbs: ["update", "patch"] + - apiGroups: ["snapshot.storage.k8s.io"] + resources: ["volumesnapshotcontents/status"] + verbs: ["update", "patch"] + - apiGroups: ["apiextensions.k8s.io"] + resources: ["customresourcedefinitions"] + verbs: ["create", "list", "watch", "delete"] + # below for resizer + - apiGroups: [""] + resources: ["persistentvolumes"] + verbs: ["update", "patch"] + - apiGroups: [""] + resources: ["persistentvolumeclaims/status"] + verbs: ["update", "patch"] + - apiGroups: ["coordination.k8s.io"] + resources: ["leases"] + verbs: ["get", "watch", "list", "delete", "update", "create"] + # below for dell-csi-replicator + {{- if hasKey .Values.controller "replication" }} + {{- if eq .Values.controller.replication.enabled true}} + - apiGroups: ["replication.storage.dell.com"] + resources: ["dellcsireplicationgroups"] + verbs: ["create", "delete", "get", "list", "patch", "update", "watch"] + - apiGroups: ["replication.storage.dell.com"] + resources: ["dellcsireplicationgroups/status"] + verbs: ["get", "patch", "update"] + - apiGroups: [""] + resources: ["configmaps"] + verbs: ["create", "delete", "get", "list", "watch", "update", "patch"] + {{- end}} + {{- end}} + {{- if eq (include "csi-isilon.isStorageCapacitySupported" .) "true" }} + - apiGroups: ["storage.k8s.io"] + resources: ["csistoragecapacities"] + verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] + - apiGroups: [""] + resources: ["pods"] + verbs: ["get"] + - apiGroups: ["apps"] + resources: ["replicasets"] + verbs: ["get"] + {{- end }} +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ .Release.Name }}-controller +subjects: + - kind: ServiceAccount + name: {{ .Release.Name }}-controller + namespace: {{ .Release.Namespace }} +roleRef: + kind: ClusterRole + name: {{ .Release.Name }}-controller + apiGroup: rbac.authorization.k8s.io +--- +kind: Deployment +apiVersion: apps/v1 +metadata: + name: {{ .Release.Name }}-controller + namespace: {{ .Release.Namespace }} + {{- if hasKey .Values "authorization" }} + {{- if eq .Values.authorization.enabled true }} + annotations: + com.dell.karavi-authorization-proxy: "true" + {{ end }} + {{ end }} +spec: + selector: + matchLabels: + app: {{ .Release.Name }}-controller + {{- if lt (.Values.controller.controllerCount | toString | atoi ) 1 -}} + {{- fail "value for .Values.controller.controllerCount should be atleast 1" }} + {{- else }} + replicas: {{ required "Must provide the number of controller instances to create." .Values.controller.controllerCount }} + {{- end }} + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 + template: + metadata: + annotations: + kubectl.kubernetes.io/default-container: "driver" + labels: + app: {{ .Release.Name }}-controller + spec: + serviceAccount: {{ .Release.Name }}-controller + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: app + operator: In + values: + - {{ .Release.Name }}-controller + topologyKey: kubernetes.io/hostname + {{ if .Values.controller.nodeSelector }} + nodeSelector: + {{- toYaml .Values.controller.nodeSelector | nindent 8 }} + {{ end }} + {{ if .Values.controller.tolerations }} + tolerations: + {{- toYaml .Values.controller.tolerations | nindent 8 }} + {{ end }} + containers: + {{- $driverSock := "csi.sock" }} + {{- $csiSidecarSuffix := "" }} + {{- $driverSockPath := printf "/var/run/csi/%s" $driverSock }} + {{- if hasKey .Values "podmon" }} + {{- if eq .Values.podmon.enabled true }} + - name: podmon + image: {{ required "Must provide the podmon container image." .Values.images.podmon.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + {{- toYaml .Values.podmon.controller.args | nindent 12 }} + env: + - name: MY_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: MY_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: MY_POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + - name: csi-isilon-config-params + mountPath: /csi-isilon-config-params + {{- end }} + {{- if hasKey .Values.controller "replication" }} + {{- if eq .Values.controller.replication.enabled true}} + - name: dell-csi-replicator + image: {{ required "Must provide the Dell CSI Replicator image." .Values.images.replication.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + - "--csi-address={{ $driverSockPath }}" + - "--leader-election=true" + - "--worker-threads=2" + - "--retry-interval-start=1s" + - "--retry-interval-max=300s" + - "--timeout=300s" + - "--context-prefix={{ .Values.controller.replication.replicationContextPrefix}}" + - "--prefix={{ .Values.controller.replication.replicationPrefix}}" + env: + - name: X_CSI_REPLICATION_CONFIG_DIR + value: /csi-isilon-config-params + - name: X_CSI_REPLICATION_CONFIG_FILE_NAME + value: driver-config-params.yaml + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + - name: csi-isilon-config-params + mountPath: /csi-isilon-config-params + {{- end }} + {{- end }} + {{- end }} + {{- if hasKey .Values.controller "resizer" }} + {{- if eq .Values.controller.resizer.enabled true }} + - name: resizer{{ $csiSidecarSuffix }} + image: {{ required "Must provide the CSI resizer container image." .Values.images.resizer.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + - "--csi-address={{ $driverSockPath }}" + - "--leader-election" + - "--timeout=120s" + - "--v=5" + {{- if hasKey .Values.controller "leaderElection" }} + {{- if hasKey .Values.controller.leaderElection "leaderElectionRenewDeadline" }} + - "--leader-election-renew-deadline={{ .Values.controller.leaderElection.leaderElectionRenewDeadline }}" + {{end}} + {{- if hasKey .Values.controller.leaderElection "leaderElectionLeaseDuration" }} + - "--leader-election-lease-duration={{ .Values.controller.leaderElection.leaderElectionLeaseDuration }}" + {{end}} + {{- if hasKey .Values.controller.leaderElection "leaderElectionLeaseDuration" }} + - "--leader-election-retry-period={{ .Values.controller.leaderElection.leaderElectionRetryPeriod }}" + {{end}} + {{end}} + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + {{ end }} + {{ end }} + - name: attacher{{ $csiSidecarSuffix }} + image: {{ required "Must provide the CSI attacher container image." .Values.images.attacher.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + - "--csi-address={{ $driverSockPath }}" + - "--v=5" + - "--leader-election" + - "--timeout=180s" + {{- if hasKey .Values.controller "leaderElection" }} + {{- if hasKey .Values.controller.leaderElection "leaderElectionRenewDeadline" }} + - "--leader-election-renew-deadline={{ .Values.controller.leaderElection.leaderElectionRenewDeadline }}" + {{end}} + {{- if hasKey .Values.controller.leaderElection "leaderElectionLeaseDuration" }} + - "--leader-election-lease-duration={{ .Values.controller.leaderElection.leaderElectionLeaseDuration }}" + {{end}} + {{- if hasKey .Values.controller.leaderElection "leaderElectionLeaseDuration" }} + - "--leader-election-retry-period={{ .Values.controller.leaderElection.leaderElectionRetryPeriod }}" + {{end}} + {{end}} + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + - name: csi-metadata-retriever + image: {{ required "Must provide the CSI metadata retriever container image." .Values.images.metadataretriever.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + command: [ "/csi-metadata-retriever" ] + env: + - name: CSI_RETRIEVER_ENDPOINT + value: /var/run/csi/csi_retriever.sock + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + {{- if hasKey .Values.controller "healthMonitor" }} + {{- if eq .Values.controller.healthMonitor.enabled true }} + - name: external-health-monitor-controller + image: {{ required "Must provide the CSI external-health-monitor-controller container image." .Values.images.healthmonitor.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + - "--csi-address={{ $driverSockPath }}" + - "--v=5" + - "--leader-election" + - "--enable-node-watcher=true" + - "--monitor-interval={{ .Values.controller.healthMonitor.interval | default "60s" }}" + - "--timeout=180s" + - "--http-endpoint=:8080" + {{- if hasKey .Values.controller "leaderElection" }} + {{- if hasKey .Values.controller.leaderElection "leaderElectionRenewDeadline" }} + - "--leader-election-renew-deadline={{ .Values.controller.leaderElection.leaderElectionRenewDeadline }}" + {{end}} + {{- if hasKey .Values.controller.leaderElection "leaderElectionLeaseDuration" }} + - "--leader-election-lease-duration={{ .Values.controller.leaderElection.leaderElectionLeaseDuration }}" + {{end}} + {{- if hasKey .Values.controller.leaderElection "leaderElectionLeaseDuration" }} + - "--leader-election-retry-period={{ .Values.controller.leaderElection.leaderElectionRetryPeriod }}" + {{end}} + {{end}} + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + {{end}} + {{end}} + - name: provisioner{{ $csiSidecarSuffix }} + image: {{ required "Must provide the CSI provisioner container image." .Values.images.provisioner.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + - "--csi-address={{ $driverSockPath }}" + - "--volume-name-prefix={{ required "Must provide a value to prefix to driver created volume names" .Values.controller.volumeNamePrefix }}" + - "--volume-name-uuid-length=10" + - "--worker-threads=5" + - "--timeout=120s" + - "--v=5" + - "--feature-gates=Topology=true" + - "--leader-election" + - "--extra-create-metadata" + - "--enable-capacity={{ (include "csi-isilon.isStorageCapacitySupported" .) | default false }}" + - "--capacity-ownerref-level=2" + - "--capacity-poll-interval={{ .Values.storageCapacity.pollInterval | default "5m" }}" + {{- if hasKey .Values.controller "leaderElection" }} + {{- if hasKey .Values.controller.leaderElection "leaderElectionRenewDeadline" }} + - "--leader-election-renew-deadline={{ .Values.controller.leaderElection.leaderElectionRenewDeadline }}" + {{end}} + {{- if hasKey .Values.controller.leaderElection "leaderElectionLeaseDuration" }} + - "--leader-election-lease-duration={{ .Values.controller.leaderElection.leaderElectionLeaseDuration }}" + {{end}} + {{- if hasKey .Values.controller.leaderElection "leaderElectionLeaseDuration" }} + - "--leader-election-retry-period={{ .Values.controller.leaderElection.leaderElectionRetryPeriod }}" + {{end}} + {{end}} + env: + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + {{- if hasKey .Values.controller "snapshot" }} + {{- if eq .Values.controller.snapshot.enabled true }} + - name: snapshotter{{ $csiSidecarSuffix }} + image: {{ required "Must provide the CSI snapshotter container image." .Values.images.snapshotter.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + - "--csi-address={{ $driverSockPath }}" + - "--timeout=120s" + - "--v=5" + - "--snapshot-name-prefix={{ required "Must privided a Snapshot Name Prefix" .Values.controller.snapshot.snapNamePrefix }}" + - "--leader-election" + {{- if hasKey .Values.controller "leaderElection" }} + {{- if hasKey .Values.controller.leaderElection "leaderElectionRenewDeadline" }} + - "--leader-election-renew-deadline={{ .Values.controller.leaderElection.leaderElectionRenewDeadline }}" + {{end}} + {{- if hasKey .Values.controller.leaderElection "leaderElectionLeaseDuration" }} + - "--leader-election-lease-duration={{ .Values.controller.leaderElection.leaderElectionLeaseDuration }}" + {{end}} + {{- if hasKey .Values.controller.leaderElection "leaderElectionLeaseDuration" }} + - "--leader-election-retry-period={{ .Values.controller.leaderElection.leaderElectionRetryPeriod }}" + {{end}} + {{end}} + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + {{end}} + {{end}} + - name: driver + image: {{ required "Must provide the Isilon driver image repository." .Values.images.driver.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + command: [ "/csi-isilon" ] + args: + - "--driver-config-params=/csi-isilon-config-params/driver-config-params.yaml" + env: + - name: CSI_ENDPOINT + value: "{{ $driverSockPath }}" + - name: CSI_RETRIEVER_ENDPOINT + value: /var/run/csi/csi_retriever.sock + - name: X_CSI_MODE + value: controller + - name: X_CSI_ISI_SKIP_CERTIFICATE_VALIDATION + value: "{{ .Values.skipCertificateValidation }}" + - name: X_CSI_ISI_AUTH_TYPE + value: "{{ .Values.isiAuthType }}" + - name: X_CSI_VERBOSE + value: "{{ .Values.verbose }}" + - name: X_CSI_ISI_PORT + value: "{{ .Values.endpointPort }}" + - name: X_CSI_ISI_AUTOPROBE + value: "{{ .Values.autoProbe }}" + - name: X_CSI_ISI_QUOTA_ENABLED + value: "{{ .Values.enableQuota }}" + - name: X_CSI_ISI_ACCESS_ZONE + value: {{ .Values.isiAccessZone }} + - name: X_CSI_CUSTOM_TOPOLOGY_ENABLED + value: "{{ .Values.enableCustomTopology }}" + - name: X_CSI_ISI_PATH + value: {{ .Values.isiPath }} + - name: X_CSI_ISI_VOLUME_PATH_PERMISSIONS + value: "{{ .Values.isiVolumePathPermissions }}" + - name: X_CSI_ISI_IGNORE_UNRESOLVABLE_HOSTS + value: "{{ .Values.ignoreUnresolvableHosts }}" + - name: X_CSI_ISI_NO_PROBE_ON_START + value: "{{ .Values.noProbeOnStart }}" + - name: X_CSI_PODMON_ENABLED + value: "{{ .Values.podmon.enabled }}" + - name: X_CSI_PODMON_API_PORT + value: "{{ .Values.podmonAPIPort }}" + {{- if eq .Values.podmon.enabled true }} + {{- range $key, $value := .Values.podmon.controller.args }} + {{- if contains "--arrayConnectivityPollRate" $value }} + - name: X_CSI_PODMON_ARRAY_CONNECTIVITY_POLL_RATE + value: "{{ (split "=" $value)._1 }}" + {{ end }} + {{ end }} + {{ end }} + {{- if hasKey .Values.controller "replication" }} + {{- if eq .Values.controller.replication.enabled true}} + - name: X_CSI_REPLICATION_CONTEXT_PREFIX + value: {{ .Values.controller.replication.replicationContextPrefix | default "powerscale"}} + - name: X_CSI_REPLICATION_PREFIX + value: {{ .Values.controller.replication.replicationPrefix | default "replication.storage.dell.com"}} + {{- end }} + {{- end }} + {{- if hasKey .Values.controller "healthMonitor" }} + {{- if eq .Values.controller.healthMonitor.enabled true }} + - name: X_CSI_HEALTH_MONITOR_ENABLED + value: "{{ .Values.controller.healthMonitor.enabled }}" + {{end}} + {{end}} + - name: X_CSI_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: SSL_CERT_DIR + value: /certs + - name: X_CSI_ISI_CONFIG_PATH + value: /isilon-configs/config + - name: X_CSI_MAX_PATH_LIMIT + value: "{{ .Values.maxPathLen }}" + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + - name: certs + mountPath: /certs + readOnly: true + - name: isilon-configs + mountPath: /isilon-configs + - name: csi-isilon-config-params + mountPath: /csi-isilon-config-params + {{- if hasKey .Values "authorization" }} + {{- if eq .Values.authorization.enabled true }} + - name: karavi-authorization-proxy + imagePullPolicy: {{ .Values.imagePullPolicy }} + image: {{ required "Must provide the authorization sidecar container image." .Values.images.authorization.image }} + env: + - name: PROXY_HOST + value: "{{ .Values.authorization.proxyHost }}" + - name: SKIP_CERTIFICATE_VALIDATION + value: "{{ .Values.authorization.skipCertificateValidation }}" + - name: PLUGIN_IDENTIFIER + value: powerscale + - name: ACCESS_TOKEN + valueFrom: + secretKeyRef: + name: proxy-authz-tokens + key: access + - name: REFRESH_TOKEN + valueFrom: + secretKeyRef: + name: proxy-authz-tokens + key: refresh + volumeMounts: + - name: karavi-authorization-config + mountPath: /etc/karavi-authorization/config + - name: proxy-server-root-certificate + mountPath: /etc/karavi-authorization/root-certificates + - name: csi-isilon-config-params + mountPath: /etc/karavi-authorization + {{ end }} + {{ end }} + volumes: + - name: socket-dir + emptyDir: + - name: certs + projected: + sources: +{{- range $i, $e := until (int .Values.certSecretCount ) }} + - secret: + name: {{ print $.Release.Name "-certs-" $e }} + items: + - key: cert-{{ $e }} + path: cert-{{ $e }} +{{- end }} + - name: isilon-configs + secret: + secretName: {{ .Release.Name }}-creds + - name: csi-isilon-config-params + configMap: + name: {{ .Release.Name }}-config-params + {{- if hasKey .Values "authorization" }} + {{- if eq .Values.authorization.enabled true }} + - name: karavi-authorization-config + secret: + secretName: karavi-authorization-config + - name: proxy-server-root-certificate + secret: + secretName: proxy-server-root-certificate + {{ end }} + {{ end }} diff --git a/charts/dell/csi-isilon/2.13.0/templates/csidriver.yaml b/charts/dell/csi-isilon/2.13.0/templates/csidriver.yaml new file mode 100644 index 0000000000..55ca3ea8b6 --- /dev/null +++ b/charts/dell/csi-isilon/2.13.0/templates/csidriver.yaml @@ -0,0 +1,14 @@ +apiVersion: storage.k8s.io/v1 +kind: CSIDriver +metadata: + name: csi-isilon.dellemc.com + labels: + security.openshift.io/csi-ephemeral-volume-profile: restricted +spec: + attachRequired: true + podInfoOnMount: true + storageCapacity: {{ (include "csi-isilon.isStorageCapacitySupported" .) | default false }} + fsGroupPolicy: {{ .Values.fsGroupPolicy }} + volumeLifecycleModes: + - Persistent + - Ephemeral diff --git a/charts/dell/csi-isilon/2.13.0/templates/driver-config-params.yaml b/charts/dell/csi-isilon/2.13.0/templates/driver-config-params.yaml new file mode 100644 index 0000000000..98671fefae --- /dev/null +++ b/charts/dell/csi-isilon/2.13.0/templates/driver-config-params.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Release.Name }}-config-params + namespace: {{ .Release.Namespace }} +data: + driver-config-params.yaml: | + CSI_LOG_LEVEL: "{{ .Values.logLevel }}" + {{ if .Values.podmon.enabled }} + PODMON_CONTROLLER_LOG_LEVEL: "{{ .Values.logLevel }}" + PODMON_CONTROLLER_LOG_FORMAT: "{{ .Values.logFormat }}" + PODMON_NODE_LOG_LEVEL: "{{ .Values.logLevel }}" + PODMON_NODE_LOG_FORMAT: "{{ .Values.logFormat }}" + {{ end }} diff --git a/charts/dell/csi-isilon/2.13.0/templates/node.yaml b/charts/dell/csi-isilon/2.13.0/templates/node.yaml new file mode 100644 index 0000000000..932f9a2404 --- /dev/null +++ b/charts/dell/csi-isilon/2.13.0/templates/node.yaml @@ -0,0 +1,348 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Release.Name }}-node + namespace: {{ .Release.Namespace }} +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ .Release.Name }}-node +rules: + - apiGroups: [""] + resources: ["persistentvolumes"] + verbs: ["create", "delete", "get", "list", "watch", "update"] + - apiGroups: [""] + resources: ["persistentvolumesclaims"] + verbs: ["get", "list", "watch", "update"] + - apiGroups: [""] + resources: ["events"] + verbs: ["get", "list", "watch", "create", "update", "patch"] + - apiGroups: [""] + resources: ["nodes"] + verbs: ["get", "list", "watch", "update", "patch"] + - apiGroups: ["storage.k8s.io"] + resources: ["volumeattachments"] + verbs: ["get", "list", "watch", "update"] + - apiGroups: ["storage.k8s.io"] + resources: ["storageclasses"] + verbs: ["get", "list", "watch"] + - apiGroups: ["storage.k8s.io"] + resources: ["volumeattachments"] + verbs: ["get", "list", "watch", "update"] + - apiGroups: [ "security.openshift.io" ] + resourceNames: [ "privileged" ] + resources: [ "securitycontextconstraints" ] + verbs: [ "use" ] + {{- if hasKey .Values "podmon" }} + {{- if eq .Values.podmon.enabled true }} + - apiGroups: [""] + resources: ["pods"] + verbs: ["get", "list", "watch", "update", "delete"] + - apiGroups: ["coordination.k8s.io"] + resources: ["leases"] + verbs: ["get", "watch", "list", "delete", "update", "create"] + {{ end }} + {{ end }} +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ .Release.Name }}-node +subjects: + - kind: ServiceAccount + name: {{ .Release.Name }}-node + namespace: {{ .Release.Namespace }} +roleRef: + kind: ClusterRole + name: {{ .Release.Name }}-node + apiGroup: rbac.authorization.k8s.io +--- +kind: DaemonSet +apiVersion: apps/v1 +metadata: + name: {{ .Release.Name }}-node + namespace: {{ .Release.Namespace }} + {{- if hasKey .Values "authorization" }} + {{- if eq .Values.authorization.enabled true }} + annotations: + com.dell.karavi-authorization-proxy: "true" + {{ end }} + {{ end }} +spec: + selector: + matchLabels: + app: {{ .Release.Name }}-node + template: + metadata: + annotations: + kubectl.kubernetes.io/default-container: "driver" + labels: + app: {{ .Release.Name }}-node +{{- if .Values.podmon.enabled }} + driver.dellemc.com: dell-storage +{{- end }} + spec: + serviceAccount: {{ .Release.Name }}-node + {{ if .Values.node.nodeSelector }} + nodeSelector: + {{- toYaml .Values.node.nodeSelector | nindent 8 }} + {{ end }} + {{ if .Values.node.tolerations }} + tolerations: + {{- toYaml .Values.node.tolerations | nindent 8 }} + {{ end }} + hostNetwork: true + dnsPolicy: {{ .Values.node.dnsPolicy }} + containers: + {{- $driverSock := "csi_sock" }} + {{- $csiSidecarSuffix := "" }} + {{- if hasKey .Values "podmon" }} + {{- if eq .Values.podmon.enabled true }} + - name: podmon + securityContext: + privileged: true + capabilities: + add: ["SYS_ADMIN"] + allowPrivilegeEscalation: true + image: {{ required "Must provide the podmon container image." .Values.images.podmon.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + {{- toYaml .Values.podmon.node.args | nindent 12 }} + env: + - name: KUBE_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + - name: X_CSI_PRIVATE_MOUNT_DIR + value: "{{ .Values.kubeletConfigDir }}/plugins/csi-isilon/disks" + - name: MY_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: MY_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: MY_POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + volumeMounts: + - name: kubelet-pods + mountPath: {{ .Values.kubeletConfigDir }}/pods + mountPropagation: "Bidirectional" + - name: driver-path + mountPath: {{ .Values.kubeletConfigDir }}/plugins/csi-isilon + mountPropagation: "Bidirectional" + - name: volumedevices-path + mountPath: {{ .Values.kubeletConfigDir }}/plugins/kubernetes.io/csi/volumeDevices + mountPropagation: "Bidirectional" + - name: dev + mountPath: /dev + - name: usr-bin + mountPath: /usr-bin + - name: var-run + mountPath: /var/run + - name: csi-isilon-config-params + mountPath: /csi-isilon-config-params + {{- end }} + {{- end }} + - name: driver + command: ["/csi-isilon"] + args: + - "--driver-config-params=/csi-isilon-config-params/driver-config-params.yaml" + securityContext: + privileged: true + capabilities: + add: ["SYS_ADMIN"] + allowPrivilegeEscalation: true + image: {{ required "Must provide the Isilon driver image repository." .Values.images.driver.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + env: + - name: CSI_ENDPOINT + value: "{{ .Values.kubeletConfigDir }}/plugins/csi-isilon/{{ $driverSock }}" + - name: X_CSI_MODE + value: node + - name: X_CSI_ISI_SKIP_CERTIFICATE_VALIDATION + value: "{{ .Values.skipCertificateValidation }}" + - name: X_CSI_ISI_AUTH_TYPE + value: "{{ .Values.isiAuthType }}" + - name: X_CSI_ALLOWED_NETWORKS + value: "{{ .Values.allowedNetworks }}" + - name: X_CSI_VERBOSE + value: "{{ .Values.verbose }}" + - name: X_CSI_PRIVATE_MOUNT_DIR + value: "{{ .Values.kubeletConfigDir }}/plugins/csi-isilon/disks" + - name: X_CSI_ISI_PORT + value: "{{ .Values.endpointPort }}" + - name: X_CSI_ISI_PATH + value: {{ .Values.isiPath }} + - name: X_CSI_ISI_NO_PROBE_ON_START + value: "{{ .Values.noProbeOnStart }}" + - name: X_CSI_ISI_AUTOPROBE + value: "{{ .Values.autoProbe }}" + - name: X_CSI_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: X_CSI_NODE_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: SSL_CERT_DIR + value: /certs + - name: X_CSI_ISI_QUOTA_ENABLED + value: "{{ .Values.enableQuota }}" + - name: X_CSI_CUSTOM_TOPOLOGY_ENABLED + value: "{{ .Values.enableCustomTopology }}" + - name: X_CSI_ISI_CONFIG_PATH + value: /isilon-configs/config + - name: X_CSI_MAX_VOLUMES_PER_NODE + value: "{{ .Values.maxIsilonVolumesPerNode }}" + - name: X_CSI_HEALTH_MONITOR_ENABLED + value: "{{ .Values.node.healthMonitor.enabled }}" + - name: X_CSI_PODMON_ENABLED + value: "{{ .Values.podmon.enabled }}" + - name: X_CSI_PODMON_API_PORT + value: "{{ .Values.podmonAPIPort }}" + {{- if eq .Values.podmon.enabled true }} + {{- range $key, $value := .Values.podmon.node.args }} + {{- if contains "--arrayConnectivityPollRate" $value }} + - name: X_CSI_PODMON_ARRAY_CONNECTIVITY_POLL_RATE + value: "{{ (split "=" $value)._1 }}" + {{ end }} + {{ end }} + {{ end }} + - name: X_CSI_MAX_PATH_LIMIT + value: "{{ .Values.maxPathLen }}" + volumeMounts: + - name: driver-path + mountPath: {{ .Values.kubeletConfigDir }}/plugins/csi-isilon + - name: volumedevices-path + mountPath: {{ .Values.kubeletConfigDir }}/plugins/kubernetes.io/csi/volumeDevices + - name: pods-path + mountPath: {{ .Values.kubeletConfigDir }}/pods + mountPropagation: "Bidirectional" + - name: dev + mountPath: /dev + - name: certs + mountPath: /certs + readOnly: true + - name: isilon-configs + mountPath: /isilon-configs + - name: csi-isilon-config-params + mountPath: /csi-isilon-config-params + - name: registrar{{ $csiSidecarSuffix }} + image: {{ required "Must provide the CSI node registrar container image." .Values.images.registrar.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + - "--v=5" + - "--csi-address=/csi/{{ $driverSock }}" + - --kubelet-registration-path={{ .Values.kubeletConfigDir }}/plugins/csi-isilon/{{ $driverSock }} + env: + - name: KUBE_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + volumeMounts: + - name: registration-dir + mountPath: /registration + - name: driver-path + mountPath: /csi + {{- if hasKey .Values "authorization" }} + {{- if eq .Values.authorization.enabled true }} + - name: karavi-authorization-proxy + imagePullPolicy: {{ .Values.imagePullPolicy }} + image: {{ required "Must provide the authorization sidecar container image." .Values.images.authorization.image }} + env: + - name: PROXY_HOST + value: "{{ .Values.authorization.proxyHost }}" + - name: SKIP_CERTIFICATE_VALIDATION + value: "{{ .Values.authorization.skipCertificateValidation }}" + - name: PLUGIN_IDENTIFIER + value: powerscale + - name: ACCESS_TOKEN + valueFrom: + secretKeyRef: + name: proxy-authz-tokens + key: access + - name: REFRESH_TOKEN + valueFrom: + secretKeyRef: + name: proxy-authz-tokens + key: refresh + volumeMounts: + - name: karavi-authorization-config + mountPath: /etc/karavi-authorization/config + - name: proxy-server-root-certificate + mountPath: /etc/karavi-authorization/root-certificates + - name: csi-isilon-config-params + mountPath: /etc/karavi-authorization + {{ end }} + {{ end }} + volumes: + - name: registration-dir + hostPath: + path: {{ .Values.kubeletConfigDir }}/plugins_registry/ + type: DirectoryOrCreate + - name: driver-path + hostPath: + path: {{ .Values.kubeletConfigDir }}/plugins/csi-isilon + type: DirectoryOrCreate + - name: volumedevices-path + hostPath: + path: {{ .Values.kubeletConfigDir }}/plugins/kubernetes.io/csi/volumeDevices + type: DirectoryOrCreate + - name: pods-path + hostPath: + path: {{ .Values.kubeletConfigDir }}/pods + type: Directory + - name: dev + hostPath: + path: /dev + type: Directory + - name: certs + projected: + sources: +{{- range $i, $e := until (int .Values.certSecretCount ) }} + - secret: + name: {{ print $.Release.Name "-certs-" $e }} + items: + - key: cert-{{ $e }} + path: cert-{{ $e }} +{{- end }} + - name: isilon-configs + secret: + secretName: {{ .Release.Name }}-creds + - name: csi-isilon-config-params + configMap: + name: {{ .Release.Name }}-config-params + {{- if hasKey .Values "authorization" }} + {{- if eq .Values.authorization.enabled true }} + - name: karavi-authorization-config + secret: + secretName: karavi-authorization-config + - name: proxy-server-root-certificate + secret: + secretName: proxy-server-root-certificate + {{ end }} + {{ end }} + {{- if hasKey .Values "podmon" }} + {{- if eq .Values.podmon.enabled true }} + - name: usr-bin + hostPath: + path: /usr/bin + type: Directory + - name: kubelet-pods + hostPath: + path: /var/lib/kubelet/pods + type: Directory + - name: var-run + hostPath: + path: /var/run + type: Directory + {{ end }} + {{ end }} diff --git a/charts/dell/csi-isilon/2.13.0/values.yaml b/charts/dell/csi-isilon/2.13.0/values.yaml new file mode 100644 index 0000000000..c83035c815 --- /dev/null +++ b/charts/dell/csi-isilon/2.13.0/values.yaml @@ -0,0 +1,432 @@ +## K8S/DRIVER ATTRIBUTES +######################## +# version: version of this values file +# Note: Do not change this value +version: "v2.13.0" + +images: + # "driver" defines the container image, used for the driver container. + driver: + image: quay.io/dell/container-storage-modules/csi-isilon:v2.13.0 + # CSI sidecars + attacher: + image: registry.k8s.io/sig-storage/csi-attacher:v4.8.0 + provisioner: + image: registry.k8s.io/sig-storage/csi-provisioner:v5.1.0 + snapshotter: + image: registry.k8s.io/sig-storage/csi-snapshotter:v8.2.0 + resizer: + image: registry.k8s.io/sig-storage/csi-resizer:v1.13.1 + registrar: + image: registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.13.0 + healthmonitor: + image: registry.k8s.io/sig-storage/csi-external-health-monitor-controller:v0.14.0 + + # CSM sidecars + replication: + image: quay.io/dell/container-storage-modules/dell-csi-replicator:v1.11.0 + podmon: + image: quay.io/dell/container-storage-modules/podmon:v1.12.0 + authorization: + image: quay.io/dell/container-storage-modules/csm-authorization-sidecar:v2.1.0 + metadataretriever: + image: quay.io/dell/container-storage-modules/csi-metadata-retriever:v1.10.0 + +# CSI driver log level +# Allowed values: "error", "warn"/"warning", "info", "debug" +# Default value: "info" +logLevel: "info" + +# certSecretCount: Represents number of certificate secrets, which user is going to create for +# ssl authentication. (isilon-cert-0..isilon-cert-n) +# Allowed values: n, where n > 0 +# Default value: None +certSecretCount: 1 + +# allowedNetworks: Custom networks for PowerScale export +# Specify list of networks which can be used for NFS I/O traffic; CIDR format should be used. +# Allowed values: list of one or more networks +# Default value: None +# Examples: [192.168.1.0/24, 192.168.100.0/22] +allowedNetworks: [] + +# maxIsilonVolumesPerNode: Specify default value for maximum number of volumes that controller can publish to the node. +# If value is zero CO SHALL decide how many volumes of this type can be published by the controller to the node. +# This limit is applicable to all the nodes in the cluster for which node label 'max-isilon-volumes-per-node' is not set. +# Allowed values: n, where n >= 0 +# Default value: 0 +maxIsilonVolumesPerNode: 0 + +# imagePullPolicy: Policy to determine if the image should be pulled prior to starting the container. +# Allowed values: +# Always: Always pull the image. +# IfNotPresent: Only pull the image if it does not already exist on the node. +# Never: Never pull the image. +# Default value: None +imagePullPolicy: IfNotPresent + +# verbose: Indicates what content of the OneFS REST API message should be logged in debug level logs +# Allowed Values: +# 0: log full content of the HTTP request and response +# 1: log without the HTTP response body +# 2: log only 1st line of the HTTP request and response +# Default value: 0 +verbose: 1 + +# Specify kubelet config dir path. +# Ensure that the config.yaml file is present at this path. +# Default value: /var/lib/kubelet +kubeletConfigDir: /var/lib/kubelet + +# enableCustomTopology: Specify if custom topology label .dellemc.com/: +# has to be used for making connection to backend PowerScale Array. +# If enableCustomTopology is set to true, then do not specify allowedTopologies in storage class. +# Allowed values: +# true : enable custom topology +# false: disable custom topology +# Default value: false +enableCustomTopology: false + +# fsGroupPolicy: Defines if the underlying volume supports changing ownership and permission of the volume before being mounted. +# Allowed values: +# ReadWriteOnceWithFSType: supports volume ownership and permissions change only if the fsType is defined +# and the volume's accessModes contains ReadWriteOnce. +# File: kubernetes may use fsGroup to change permissions and ownership of the volume +# to match user requested fsGroup in the pod's security policy regardless of fstype or access mode. +# None: volumes will be mounted with no modifications. +# Default value: ReadWriteOnceWithFSType +fsGroupPolicy: ReadWriteOnceWithFSType + +# podmonAPIPort: Defines the port to be used within the kubernetes cluster +# Allowed values: +# Any valid and free port. +# Default value: 8083 +podmonAPIPort: 8083 + +# maxPathLen: this parameter is used for setting the maximum Path length for the given volume. +# Default value: 192 +# Examples: 192, 256 +maxPathLen: 192 + +# controller: configure controller pod specific parameters +controller: + # controllerCount: defines the number of csi-powerscale controller pods to deploy to + # the Kubernetes release. + # Allowed values: n, where n > 0 + # Default value: None + controllerCount: 2 + + # volumeNamePrefix: Prefix of PersistentVolume names created + # Allowed values: string + # Default value: csivol + # Examples: "k8s", "app1" + volumeNamePrefix: csivol + + # leaderElection: configure leader election parameters + leaderElection: + # Duration, that non-leader candidates will wait to force acquire leadership + # Allowed values: Duration, in seconds. Must be greater than leaderElectionRenewDeadline + # Default value: 15s + leaderElectionLeaseDuration: 15s + + # Duration, that the acting leader will retry refreshing leadership before giving up + # Allowed values: Duration, in seconds. Must be greater than leaderElectionRetryPeriod + # Default value: 10s + leaderElectionRenewDeadline: 10s + + # Duration, the LeaderElector clients should wait between tries of actions. + # Allowed values: Duration, in seconds + # Default value: 5s + leaderElectionRetryPeriod: 5s + + # replication: allows to configure replication + # Replication CRDs must be installed before installing driver + replication: + # enabled: Enable/Disable replication feature + # Allowed values: + # true: enable replication feature(install dell-csi-replicator sidecar) + # false: disable replication feature(do not install dell-csi-replicator sidecar) + # Default value: false + enabled: false + + # replicationContextPrefix: prefix to use for naming of resources created by replication feature + # Allowed values: string + # Default value: powerscale + replicationContextPrefix: "powerscale" + + # replicationPrefix: prefix to prepend to storage classes parameters + # Allowed values: string + # Default value: replication.storage.dell.com + replicationPrefix: "replication.storage.dell.com" + + snapshot: + # enabled: Enable/Disable volume snapshot feature + # Allowed values: + # true: enable volume snapshot feature(install snapshotter sidecar) + # false: disable volume snapshot feature(do not install snapshotter sidecar) + # Default value: None + enabled: true + + # snapNamePrefix: Prefix to apply to the names of a created snapshots + # Allowed values: string + # Default value: csi-snap + # Examples: "snap", "snapshot" + snapNamePrefix: csi-snap + + resizer: + # enabled: Enable/Disable volume expansion feature + # Allowed values: + # true: enable volume expansion feature(install resizer sidecar) + # false: disable volume snapshot feature(do not install resizer sidecar) + # Default value: None + enabled: true + + healthMonitor: + # enabled: Enable/Disable health monitor of CSI volumes- volume status, volume condition + # Allowed values: + # true: enable checking of health condition of CSI volumes + # false: disable checking of health condition of CSI volumes + # Default value: None + enabled: false + + # interval: Interval of monitoring volume health condition + # Allowed values: Number followed by unit of time (s,m,h) + # Default value: 60s + interval: 60s + + # nodeSelector: Define node selection constraints for pods of controller deployment. + # For the pod to be eligible to run on a node, the node must have each + # of the indicated key-value pairs as labels. + # Leave as blank to consider all nodes + # Allowed values: map of key-value pairs + # Default value: None + nodeSelector: + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/master taint + # node-role.kubernetes.io/master: "" + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/control-plane taint + # node-role.kubernetes.io/control-plane: "" + + # tolerations: Define tolerations for the controller deployment, if required. + # Default value: None + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/master taint + tolerations: + # - key: "node-role.kubernetes.io/master" + # operator: "Exists" + # effect: "NoSchedule" + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/control-plane taint + # tolerations: + # - key: "node-role.kubernetes.io/control-plane" + # operator: "Exists" + # effect: "NoSchedule" + +# node: configure node pod specific parameters +node: + # nodeSelector: Define node selection constraints for pods of node daemonset + # For the pod to be eligible to run on a node, the node must have each + # of the indicated key-value pairs as labels. + # Leave as blank to consider all nodes + # Allowed values: map of key-value pairs + # Default value: None + nodeSelector: + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/master taint + # node-role.kubernetes.io/master: "" + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/control-plane taint + # node-role.kubernetes.io/control-plane: "" + + # tolerations: Define tolerations for the node daemonset, if required. + # Default value: None + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/master taint + tolerations: + # - key: "node.kubernetes.io/memory-pressure" + # operator: "Exists" + # effect: "NoExecute" + # - key: "node.kubernetes.io/disk-pressure" + # operator: "Exists" + # effect: "NoExecute" + # - key: "node.kubernetes.io/network-unavailable" + # operator: "Exists" + # effect: "NoExecute" + # - key: "node-role.kubernetes.io/master" + # operator: "Exists" + # effect: "NoSchedule" + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/control-plane taint + # tolerations: + # - key: "node-role.kubernetes.io/control-plane" + # operator: "Exists" + # effect: "NoSchedule" + + # Uncomment if CSM for Resiliency and CSI Driver pods monitor are enabled + # tolerations: + # - key: "offline.vxflexos.storage.dell.com" + # operator: "Exists" + # effect: "NoSchedule" + # - key: "vxflexos.podmon.storage.dell.com" + # operator: "Exists" + # effect: "NoSchedule" + # - key: "offline.unity.storage.dell.com" + # operator: "Exists" + # effect: "NoSchedule" + # - key: "unity.podmon.storage.dell.com" + # operator: "Exists" + # effect: "NoSchedule" + # - key: "offline.isilon.storage.dell.com" + # operator: "Exists" + # effect: "NoSchedule" + # - key: "isilon.podmon.storage.dell.com" + # operator: "Exists" + # effect: "NoSchedule" + + # dnsPolicy: Determines the DNS Policy of the Node service. + # Allowed values: + # Default: The Pod inherits the name resolution configuration from the node that the pods run on. + # ClusterFirst: Any DNS query that does not match the configured cluster domain suffix, such as "www.kubernetes.io", + # is forwarded to the upstream nameserver inherited from the node. + # ClusterFirstWithHostNet: For Pods running with hostNetwork, you should explicitly set this DNS policy. + # None: It allows a Pod to ignore DNS settings from the Kubernetes environment. + # All DNS settings are supposed to be provided using the dnsConfig field in the Pod Spec. + # Default value: ClusterFirst + # ClusterFirstWithHostNet is the recommended DNS policy. + # Prior to v1.5 of the driver, the default DNS policy was ClusterFirst. + # In certain scenarios, users might need to change the default dnsPolicy. + dnsPolicy: ClusterFirstWithHostNet + + healthMonitor: + # enabled: Enable/Disable health monitor of CSI volumes- volume usage, volume condition + # Allowed values: + # true: enable checking of health condition of CSI volumes + # false: disable checking of health condition of CSI volumes + # Default value: None + enabled: false + +## PLATFORM ATTRIBUTES +###################### +# endpointPort: Specify the HTTPs port number of the PowerScale OneFS API server +# Formerly this attribute was named as "isiPort" +# This value acts as a default value for endpointPort, if not specified for a cluster config in secret +# If authorization is enabled, endpointPort must match the port specified in the endpoint parameter of the karavi-authorization-config secret +# Allowed value: valid port number +# Default value: 8080 +endpointPort: 8080 + +# skipCertificateValidation: Specify whether the PowerScale OneFS API server's certificate chain and host name should be verified. +# Formerly this attribute was named as "isiInsecure" +# This value acts as a default value for skipCertificateValidation, if not specified for a cluster config in secret +# Allowed values: +# true: skip OneFS API server's certificate verification +# false: verify OneFS API server's certificates +# Default value: false +skipCertificateValidation: true + +# isiAuthType: Indicates whether the authentication will be session-based or basic. +# Allowed values: +# 0: enables basic Authentication +# 1: enables session-based Authentication +# Default value: 0 +isiAuthType: 0 + +# isiAccessZone: The name of the access zone a volume can be created in. +# If storageclass is missing with AccessZone parameter, then value of isiAccessZone is used for the same. +# Default value: System +# Examples: System, zone1 +isiAccessZone: System + +# enableQuota: Indicates whether the provisioner should attempt to set (later unset) quota +# on a newly provisioned volume. +# This requires SmartQuotas to be enabled on PowerScale cluster. +# Allowed values: +# true: set quota for volume +# false: do not set quota for volume +enableQuota: true + +# isiPath: The base path for the volumes to be created on PowerScale cluster. +# This value acts as a default value for isiPath, if not specified for a cluster config in secret +# Ensure that this path exists on PowerScale cluster. +# Allowed values: unix absolute path +# Default value: /ifs +# Examples: /ifs/data/csi, /ifs/engineering +isiPath: /ifs/data/csi + +# isiVolumePathPermissions: The permissions for isi volume directory path +# This value acts as a default value for isiVolumePathPermissions, if not specified for a cluster config in secret +# Allowed values: valid octal mode number +# Default value: "0777" +# Examples: "0777", "777", "0755" +isiVolumePathPermissions: "0777" + +# ignoreUnresolvableHosts: Ignore unresolvable hosts on the OneFS +# When set to true, OneFS allows new host to add to existing export list though any of the existing hosts from the +# same exports are unresolvable/doesn't exist anymore. +# Allowed values: +# true: ignore existing unresolvable hosts and append new host to the existing export +# false: exhibits OneFS default behavior i.e. if any of existing hosts are unresolvable while adding new one it fails +# Default value: false +ignoreUnresolvableHosts: false + +# noProbeOnStart: Indicates whether the controller/node should probe all the PowerScale clusters during driver initialization +# When set to true, the driver will not set node labels, please manually add +# the label .dellemc.com/: on the nodes for each of the clusters reachable from the node. +# Allowed values: +# true : do not probe all PowerScale clusters during driver initialization +# false: probe all PowerScale clusters during driver initialization +# Default value: false +noProbeOnStart: false + +# autoProbe: automatically probe the PowerScale cluster if not done already during CSI calls. +# Allowed values: +# true : enable auto probe. +# false: disable auto probe. +# Default value: false +autoProbe: true + +authorization: + enabled: false + # proxyHost: hostname of the csm-authorization server + # Default value: None + proxyHost: + # skipCertificateValidation: certificate validation of the csm-authorization server + # Allowed Values: + # "true" - TLS certificate verification will be skipped + # "false" - TLS certificate will be verified + # Default value: "true" + skipCertificateValidation: true + +# Storage Capacity Tracking +# Note: Capacity tracking is supported in kubernetes v1.24 and above, this feature will be automatically disabled in older versions. +storageCapacity: + # enabled : Enable/Disable storage capacity tracking + # Allowed values: + # true: enable storage capacity tracking + # false: disable storage capacity tracking + # Default value: true + enabled: true + # pollInterval : Configure how often external-provisioner polls the driver to detect changed capacity + # Allowed values: 1m,2m,3m,...,10m,...,60m etc + # Default value: 5m + pollInterval: 5m + +# Enable this feature only after contact support for additional information +podmon: + enabled: false + controller: + args: + - "--csisock=unix:/var/run/csi/csi.sock" + - "--labelvalue=csi-isilon" + - "--arrayConnectivityPollRate=60" + - "--driverPath=csi-isilon.dellemc.com" + - "--mode=controller" + - "--skipArrayConnectionValidation=false" + - "--driver-config-params=/csi-isilon-config-params/driver-config-params.yaml" + - "--driverPodLabelValue=dell-storage" + - "--ignoreVolumelessPods=false" + + node: + args: + - "--csisock=unix:/var/lib/kubelet/plugins/csi-isilon/csi_sock" + - "--labelvalue=csi-isilon" + - "--arrayConnectivityPollRate=60" + - "--driverPath=csi-isilon.dellemc.com" + - "--mode=node" + - "--leaderelection=false" + - "--driver-config-params=/csi-isilon-config-params/driver-config-params.yaml" + - "--driverPodLabelValue=dell-storage" + - "--ignoreVolumelessPods=false" diff --git a/charts/dell/csi-powermax/2.13.0/Chart.yaml b/charts/dell/csi-powermax/2.13.0/Chart.yaml new file mode 100644 index 0000000000..d9ffd40596 --- /dev/null +++ b/charts/dell/csi-powermax/2.13.0/Chart.yaml @@ -0,0 +1,28 @@ +annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Dell CSI PowerMax + catalog.cattle.io/kube-version: '>= 1.23.0' + catalog.cattle.io/release-name: "" +apiVersion: v2 +appVersion: 2.13.0 +dependencies: +- condition: required + name: csireverseproxy + repository: "" + version: 2.12.0 +description: 'PowerMax CSI (Container Storage Interface) driver Kubernetes integration. + This chart includes everything required to provision via CSI as well as a PowerMax + StorageClass. ' +home: https://github.com/dell/csi-powermax +icon: file://assets/icons/csi-powermax.png +keywords: +- csi +- storage +kubeVersion: '>= 1.23.0' +maintainers: +- name: DellEMC +name: csi-powermax +sources: +- https://github.com/dell/csi-powermax +type: application +version: 2.13.0 diff --git a/charts/dell/csi-powermax/2.13.0/app-readme.md b/charts/dell/csi-powermax/2.13.0/app-readme.md new file mode 100644 index 0000000000..9f253ea7ea --- /dev/null +++ b/charts/dell/csi-powermax/2.13.0/app-readme.md @@ -0,0 +1,16 @@ + + +## Prerequisites + +1. Create a namespace where you want to install the driver (e.g. "csi-powermax"). You can choose any name for the namespace, but make sure to align to the same namespace during the whole installation. +2. Create a secret named "powermax-creds" in the namespace created above. Sample [secret.yaml](https://github.com/dell/csi-powermax/blob/main/samples/secret/secret.yaml). + >Secret must be of type opaque. +3. Install Cert Manager + >Create issuer for Cert Manager + >Create TLS Certificate in powermax namespace +4. Create storage classes using ones from [samples](https://github.com/dell/csi-powermax/tree/main/samples/storageclass) folder as an example. + > If you do not specify `arrayID` parameter in the storage class then the array that was specified as the default would be used for provisioning volumes. +5. Install the chart with the name "csi-powermax". The value.yaml file used during installation can be found [here](https://github.com/dell/csi-powermax/blob/main/helm/csi-powermax/values.yaml) + +The table [here](https://dell.github.io/csm-docs/docs/csidriver/installation/helm/powermax/#:~:text=powermax%2Dsettings.yaml-,Parameter,Default,-global) lists the configurable parameters of the chart and their default values. + diff --git a/charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/Chart.yaml b/charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/Chart.yaml new file mode 100644 index 0000000000..af49b678f6 --- /dev/null +++ b/charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +appVersion: 2.12.0 +description: A Helm chart for CSI PowerMax ReverseProxy +name: csireverseproxy +type: application +version: 2.12.0 diff --git a/charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/conf/config.yaml b/charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/conf/config.yaml new file mode 100644 index 0000000000..b788271a13 --- /dev/null +++ b/charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/conf/config.yaml @@ -0,0 +1,38 @@ +port: {{ .Values.port }} +logLevel: {{ .Values.global.logLevel | default "debug" }} +logFormat: {{ .Values.global.logFormat | default "TEXT" }} +config: + {{- $defaultProxyCreds := .Values.global.defaultCredentialsSecret }} + storageArrays: + {{- $_ := first .Values.global.storageArrays }} + {{- range $index, $value := .Values.global.storageArrays }} + - storageArrayId: {{ required "Must provide a storage array id." $value.storageArrayId | toJson }} + primaryURL: {{ required "Must provide a primary Unisphere HTTPS endpoint." $value.endpoint }} + backupURL: {{ $value.backupEndpoint | default "" }} + proxyCredentialSecrets: + - {{ required "Must provide secret for proxy credentials" $defaultProxyCreds }} + {{- end }} + managementServers: + {{- $_ := first .Values.global.managementServers }} + {{- range $index, $value := .Values.global.managementServers }} + - url: {{required "Must provide a Unisphere HTTPS endpoint." $value.endpoint }} + {{- if empty $value.credentialsSecret }} + arrayCredentialSecret: {{ required "Must provide an array credential secret" $defaultProxyCreds }} + {{- else }} + arrayCredentialSecret: {{ required "Must provide an array credential secret" $value.credentialsSecret }} + {{- end }} + {{- if $value.certSecret }} + {{- $check := toString $value.skipCertificateValidation }} + skipCertificateValidation: {{ ternary $value.skipCertificateValidation true (or (eq $check "true") (eq $check "false")) }} + {{- else }} + skipCertificateValidation: true + {{- end }} + certSecret: {{ $value.certSecret | default "" }} + {{- if $value.limits }} + limits: + maxActiveRead: {{ $value.limits.maxActiveRead | default 0 }} + maxActiveWrite: {{ $value.limits.maxActiveWrite | default 0 }} + maxOutStandingRead: {{ $value.limits.maxOutStandingRead | default 0 }} + maxOutStandingWrite: {{ $value.limits.maxOutStandingWrite | default 0 }} + {{- end }} + {{- end }} \ No newline at end of file diff --git a/charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/templates/_helpers.tpl b/charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/templates/_helpers.tpl new file mode 100644 index 0000000000..000bd37286 --- /dev/null +++ b/charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/templates/_helpers.tpl @@ -0,0 +1,9 @@ +{{/* +Namespace for all resources to be installed into +If not defined in values file then the helm release namespace is used +By default this is not set so the helm release namespace will be used +*/}} + +{{- define "custom.namespace" -}} + {{ .Values.namespace | default .Release.Namespace }} +{{- end -}} \ No newline at end of file diff --git a/charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/templates/certificate.yaml b/charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/templates/certificate.yaml new file mode 100644 index 0000000000..e91882bddd --- /dev/null +++ b/charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/templates/certificate.yaml @@ -0,0 +1,72 @@ +# If the csireverseproxy cert and key are provided, deploy a CA Issuer using the cert and key +{{- if ne .Values.certManager.selfSignedCert true }} +apiVersion: v1 +data: + tls.crt: {{ .Values.certManager.certificateFile }} + tls.key: {{ .Values.certManager.privateKeyFile }} +kind: Secret +type: kubernetes.io/tls +metadata: + name: csirevproxy-tls-secret + namespace: {{ .Release.Namespace }} + +--- +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: csirevproxy-issuer + namespace: {{ .Release.Namespace }} +spec: + ca: + secretName: csirevproxy-tls-secret +--- +{{- else }} +# deploy a selfsigned-issuer +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: selfsigned-issuer + namespace: {{ .Release.Namespace }} +spec: + selfSigned: {} + +--- +{{- end }} + +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: csirevproxy-tls-cert + namespace: {{ .Release.Namespace }} +spec: + secretName: csirevproxy-tls-secret + commonName: powermax-reverseproxy + duration: 2160h # 90d + renewBefore: 360h # 15d + subject: + organizations: + - dellemc + isCA: false + privateKey: + algorithm: RSA + encoding: PKCS1 + size: 2048 + usages: + - server auth + - client auth + dnsNames: + - powermax-reverseproxy + - powermax-reverseproxy.powermax.svc.cluster.local + - reverseproxy + ipAddresses: + - 0.0.0.0 + issuerRef: + {{- if ne .Values.certManager.selfSignedCert true }} + name: csirevproxy-issuer + {{- else }} + name: selfsigned-issuer + {{- end }} + kind: Issuer + group: cert-manager.io +--- diff --git a/charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/templates/configmap.yaml b/charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/templates/configmap.yaml new file mode 100644 index 0000000000..27938ea42e --- /dev/null +++ b/charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/templates/configmap.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Release.Name }}-reverseproxy-config + namespace: {{ .Release.Namespace }} +data: +{{ tpl (.Files.Glob "conf/config.yaml").AsConfig . | indent 2 }} diff --git a/charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/templates/reverseproxy-rbac.yaml b/charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/templates/reverseproxy-rbac.yaml new file mode 100644 index 0000000000..e6530cf1ae --- /dev/null +++ b/charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/templates/reverseproxy-rbac.yaml @@ -0,0 +1,25 @@ +{{- if ne .Values.deployAsSidecar true }} +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ .Release.Name }}-reverseproxy + namespace: {{ .Release.Namespace }} +rules: + - apiGroups: [""] + resources: ["secrets"] + verbs: ["list", "watch", "get"] +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ .Release.Name }}-reverseproxy + namespace: {{ .Release.Namespace }} +subjects: + - kind: ServiceAccount + name: {{ .Release.Name }}-reverseproxy + namespace: {{ .Release.Namespace }} +roleRef: + kind: Role + name: {{ .Release.Name }}-reverseproxy + apiGroup: rbac.authorization.k8s.io +{{- end }} diff --git a/charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/templates/reverseproxy.yaml b/charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/templates/reverseproxy.yaml new file mode 100644 index 0000000000..53d291ca45 --- /dev/null +++ b/charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/templates/reverseproxy.yaml @@ -0,0 +1,50 @@ +{{- if ne .Values.deployAsSidecar true }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }}-reverseproxy + namespace: {{ .Release.Namespace }} +spec: + replicas: 1 + selector: + matchLabels: + name: {{ .Release.Name }}-reverseproxy + template: + metadata: + labels: + name: {{ .Release.Name }}-reverseproxy + spec: + serviceAccountName: {{ .Release.Name }}-reverseproxy + containers: + - name: csireverseproxy + image: {{ required "Must provided an image for reverseproxy container." .Values.image }} + imagePullPolicy: Always + env: + - name: X_CSI_REVPROXY_CONFIG_DIR + value: /etc/config/configmap + - name: X_CSI_REVPROXY_CONFIG_FILE_NAME + value: config.yaml + - name: X_CSI_REVRPOXY_IN_CLUSTER + value: "true" + - name: X_CSI_REVPROXY_TLS_CERT_DIR + value: /app/tls + - name: X_CSI_REVPROXY_WATCH_NAMESPACE + value: {{ .Release.Namespace }} + volumeMounts: + - name: configmap-volume + mountPath: /etc/config/configmap + - name: tls-secret + mountPath: /app/tls + - name: cert-dir + mountPath: /app/certs + volumes: + - name: configmap-volume + configMap: + name: {{ .Release.Name }}-reverseproxy-config + optional: true + - name: tls-secret + secret: + secretName: {{ .Values.tlsSecret }} + - name: cert-dir + emptyDir: +{{- end }} \ No newline at end of file diff --git a/charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/templates/service.yaml b/charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/templates/service.yaml new file mode 100644 index 0000000000..ea1b34e433 --- /dev/null +++ b/charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/templates/service.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }}-reverseproxy + namespace: {{ .Release.Namespace }} +spec: + ports: + - port: {{ .Values.port }} + protocol: TCP + targetPort: 2222 + selector: + {{- if eq .Values.deployAsSidecar true}} + name: {{ .Release.Name }}-controller + {{- else }} + name: {{ .Release.Name }}-reverseproxy + {{- end }} + type: ClusterIP diff --git a/charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/templates/serviceaccount.yaml b/charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/templates/serviceaccount.yaml new file mode 100644 index 0000000000..aa37a367c7 --- /dev/null +++ b/charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/templates/serviceaccount.yaml @@ -0,0 +1,7 @@ +{{- if ne .Values.deployAsSidecar true }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Release.Name }}-reverseproxy + namespace: {{ .Release.Namespace }} +{{- end }} \ No newline at end of file diff --git a/charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/values.yaml b/charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/values.yaml new file mode 100644 index 0000000000..f3cf87b0c8 --- /dev/null +++ b/charts/dell/csi-powermax/2.13.0/charts/csireverseproxy/values.yaml @@ -0,0 +1,7 @@ +image: quay.io/dell/container-storage-modules/csipowermax-reverseproxy:v2.12.0 +port: 2222 + +# TLS secret which is used for setting up the proxy HTTPS server +# Don't change this value unless really necessary +# If this value is modified, then the installation script will have to be modified +tlsSecret: "csirevproxy-tls-secret" diff --git a/charts/dell/csi-powermax/2.13.0/templates/_helpers.tpl b/charts/dell/csi-powermax/2.13.0/templates/_helpers.tpl new file mode 100644 index 0000000000..80bf5d708c --- /dev/null +++ b/charts/dell/csi-powermax/2.13.0/templates/_helpers.tpl @@ -0,0 +1,8 @@ + +{{- define "csi-powermax.isStorageCapacitySupported" -}} +{{- if eq .Values.storageCapacity.enabled true -}} + {{- if and (eq .Capabilities.KubeVersion.Major "1") (ge (trimSuffix "+" .Capabilities.KubeVersion.Minor) "24") -}} + {{- true -}} + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/dell/csi-powermax/2.13.0/templates/controller.yaml b/charts/dell/csi-powermax/2.13.0/templates/controller.yaml new file mode 100644 index 0000000000..fc96b1b9d9 --- /dev/null +++ b/charts/dell/csi-powermax/2.13.0/templates/controller.yaml @@ -0,0 +1,568 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{.Release.Name}}-controller + namespace: {{ .Release.Namespace }} +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + {{- if eq .Values.customDriverName.enabled true}} + name: {{printf "%s-%s-controller" .Release.Namespace .Values.customDriverName.value}} + {{- else }} + name: {{ .Release.Name }}-controller + {{- end }} +rules: + - apiGroups: ["coordination.k8s.io"] + resources: ["leases"] + verbs: ["get", "watch", "list", "delete", "update", "create"] + - apiGroups: [""] + resources: ["events"] + verbs: ["list", "watch", "create", "update", "patch"] + - apiGroups: [""] + resources: ["nodes"] + {{- if eq .Values.podmon.enabled true }} + verbs: [ "get", "list", "watch", "patch" ] + {{- else }} + verbs: [ "get", "list", "watch" ] + {{- end }} + - apiGroups: [""] + resources: ["persistentvolumes"] + verbs: ["get", "list", "watch", "create", "delete", "update", "patch" ] + - apiGroups: [""] + resources: ["persistentvolumeclaims"] + verbs: ["create", "delete", "get", "list", "watch", "update", "patch"] + - apiGroups: ["storage.k8s.io"] + resources: ["storageclasses"] + verbs: ["get", "list", "watch"] + - apiGroups: [""] + resources: ["persistentvolumeclaims/status"] + verbs: ["update", "patch"] + - apiGroups: [ "storage.k8s.io" ] + resources: [ "volumeattachments" ] + {{- if eq .Values.podmon.enabled true }} + verbs: [ "get", "list", "watch", "update", "patch", "delete" ] + {{- else }} + verbs: [ "get", "list", "watch", "update", "patch" ] + {{- end }} + - apiGroups: ["storage.k8s.io"] + resources: ["csinodes"] + verbs: ["get", "list", "watch", "update"] + - apiGroups: ["storage.k8s.io"] + resources: ["volumeattachments/status"] + verbs: ["patch"] + - apiGroups: ["csi.storage.k8s.io"] + resources: ["csinodeinfos"] + verbs: ["get", "list", "watch"] + - apiGroups: [ "" ] + resources: [ "pods" ] + {{- if hasKey .Values "podmon" }} + {{- if eq .Values.podmon.enabled true }} + verbs: [ "get", "list", "watch", "update", "delete" ] + {{- else }} + verbs: [ "get", "list", "watch" ] + {{- end }} + {{- end }} +# below for snapshotter + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "list", "watch"] + - apiGroups: ["snapshot.storage.k8s.io"] + resources: ["volumesnapshotcontents"] + verbs: ["get", "list", "watch", "update", "create", "delete", "patch"] + - apiGroups: ["snapshot.storage.k8s.io"] + resources: ["volumesnapshotcontents/status"] + verbs: ["update", "patch"] + - apiGroups: ["snapshot.storage.k8s.io"] + resources: ["volumesnapshotclasses"] + verbs: ["get", "list", "watch"] + - apiGroups: ["snapshot.storage.k8s.io"] + resources: ["volumesnapshots", "volumesnapshots/status"] + verbs: ["get", "list", "watch", "update"] + - apiGroups: ["apiextensions.k8s.io"] + resources: ["customresourcedefinitions"] + verbs: ["create", "list", "watch", "delete"] +# below for dell-csi-replicator + {{- if eq .Values.replication.enabled true}} + - apiGroups: ["replication.storage.dell.com"] + resources: ["dellcsireplicationgroups"] + verbs: ["create", "delete", "get", "list", "patch", "update", "watch"] + - apiGroups: ["replication.storage.dell.com"] + resources: ["dellcsireplicationgroups/status"] + verbs: ["get", "patch", "update"] + - apiGroups: [""] + resources: ["configmaps"] + verbs: ["create", "delete", "get", "list", "watch", "update", "patch"] + {{- end}} + # below for dell-csi-migrator + {{- if eq .Values.migration.enabled true}} + - apiGroups: [ "replication.storage.dell.com" ] + resources: [ "dellcsimigrationgroups" ] + verbs: [ "create", "delete", "get", "list", "patch", "update", "watch" ] + - apiGroups: [ "replication.storage.dell.com" ] + resources: [ "dellcsimigrationgroups/status" ] + verbs: [ "get", "patch", "update" ] + - apiGroups: [""] + resources: ["configmaps"] + verbs: ["create", "delete", "get", "list", "watch", "update", "patch"] + {{- end}} + # Permissions for Storage Capacity + {{- if eq (include "csi-powermax.isStorageCapacitySupported" .) "true" }} + - apiGroups: ["storage.k8s.io"] + resources: ["csistoragecapacities"] + verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] + - apiGroups: ["apps"] + resources: ["replicasets"] + verbs: ["get"] + {{- end }} +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + {{- if eq .Values.customDriverName.enabled true}} + name: {{ printf "%s-%s-controller" .Release.Namespace .Values.customDriverName.value }} + {{- else }} + name: {{ .Release.Name }}-controller + {{- end }} +subjects: + - kind: ServiceAccount + name: {{ .Release.Name }}-controller + namespace: {{ .Release.Namespace }} +roleRef: + kind: ClusterRole + {{- if eq .Values.customDriverName.enabled true}} + name: {{ printf "%s-%s-controller" .Release.Namespace .Values.customDriverName.value }} + {{- else }} + name: {{ .Release.Name }}-controller + {{- end }} + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }}-controller + namespace: {{ .Release.Namespace }} + {{- if hasKey .Values "authorization" }} + {{- if eq .Values.authorization.enabled true }} + annotations: + com.dell.karavi-authorization-proxy: "true" + {{ end }} + {{ end }} +spec: + replicas: {{ required "Must provide the number of controller instances to create." .Values.controller.controllerCount }} + selector: + matchLabels: + name: {{ .Release.Name }}-controller + template: + metadata: + annotations: + kubectl.kubernetes.io/default-container: "driver" + labels: + name: {{ .Release.Name }}-controller + spec: + {{ if .Values.controller.nodeSelector }} + nodeSelector: + {{- toYaml .Values.controller.nodeSelector | nindent 8 }} + {{ end }} + {{ if .Values.controller.tolerations }} + tolerations: + {{- toYaml .Values.controller.tolerations | nindent 6 }} + {{ end }} + serviceAccountName: {{ .Release.Name }}-controller + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: name + operator: In + values: + - {{ .Release.Name }}-controller + topologyKey: kubernetes.io/hostname + containers: + {{- if hasKey .Values "podmon" }} + {{- if eq .Values.podmon.enabled true }} + - name: podmon + image: {{ required "Must provide the podmon container image." .Values.images.podmon.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + {{- toYaml .Values.podmon.controller.args | nindent 12 }} + env: + - name: MY_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: MY_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: MY_POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + - name: powermax-config-params + mountPath: /powermax-config-params + {{- end }} + {{- end }} + - name: attacher + image: {{ required "Must provide the CSI attacher container image." .Values.images.attacher.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + - "--csi-address=$(ADDRESS)" + - "--v=5" + - "--timeout=180s" + - "--worker-threads=6" + - "--leader-election" + env: + - name: ADDRESS + value: /var/run/csi/csi.sock + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + - name: provisioner + image: {{ required "Must provide the CSI provisioner container image." .Values.images.provisioner.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + - "--csi-address=$(ADDRESS)" + - "--volume-name-prefix={{ required "Must provide a Volume Name Prefix." .Values.controller.volumeNamePrefix }}" + - "--volume-name-uuid-length=10" + - "--timeout=180s" + - "--worker-threads=6" + - "--v=5" + - "--default-fstype={{ .Values.defaultFsType | default "ext4" }}" + - "--leader-election" + - "--extra-create-metadata" + - "--feature-gates=Topology=true" + - "--enable-capacity={{ (include "csi-powermax.isStorageCapacitySupported" .) | default false }}" + - "--capacity-ownerref-level=2" + - "--capacity-poll-interval={{ .Values.storageCapacity.pollInterval | default "5m" }}" + env: + - name: ADDRESS + value: /var/run/csi/csi.sock + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + {{- if hasKey .Values.controller "snapshot" }} + {{- if eq .Values.controller.snapshot.enabled true }} + - name: snapshotter + image: {{ required "Must provide the CSI snapshotter container image." .Values.images.snapshotter.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + - "--csi-address=$(ADDRESS)" + - "--v=5" + - "--snapshot-name-prefix={{ required "Must provide a Snapshot Name Prefix" .Values.controller.snapshot.snapNamePrefix }}" + - "--snapshot-name-uuid-length=10" + - "--timeout=180s" + - "--leader-election" + env: + - name: ADDRESS + value: /var/run/csi/csi.sock + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + {{- end }} + {{- end }} + {{- if eq .Values.replication.enabled true}} + - name: dell-csi-replicator + image: {{ required "Must provide the Dell CSI Replicator Resizer image." .Values.images.replication.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + - "--csi-address=$(ADDRESS)" + - "--leader-election=true" + - "--worker-threads=2" + - "--retry-interval-start=1s" + - "--retry-interval-max=300s" + - "--timeout=300s" + - "--context-prefix={{ .Values.replication.replicationContextPrefix}}" + - "--prefix={{ .Values.replication.replicationPrefix}}" + env: + - name: ADDRESS + value: /var/run/csi/csi.sock + - name: X_CSI_REPLICATION_CONFIG_DIR + value: /powermax-config-params + - name: X_CSI_REPLICATION_CONFIG_FILE_NAME + value: driver-config-params.yaml + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + - name: powermax-config-params + mountPath: /powermax-config-params + {{- end }} + {{- if eq .Values.migration.enabled true}} + - name: dell-csi-migrator + image: {{ required "Must provide the Dell CSI Migrator Resizer image." .Values.images.migration.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + - "--csi-address=$(ADDRESS)" + - "--leader-election=true" + - "--worker-threads=2" + - "--retry-interval-start=1s" + - "--retry-interval-max=300s" + - "--timeout=300s" + - "--prefix={{ .Values.migration.migrationPrefix}}" + env: + - name: ADDRESS + value: /var/run/csi/csi.sock + - name: X_CSI_REPLICATION_CONFIG_DIR + value: /powermax-config-params + - name: X_CSI_REPLICATION_CONFIG_FILE_NAME + value: driver-config-params.yaml + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + - name: powermax-config-params + mountPath: /powermax-config-params + {{- end }} + {{- if hasKey .Values.controller "resizer" }} + {{- if eq .Values.controller.resizer.enabled true }} + - name: resizer + image: {{ required "Must provide the CSI resizer container image." .Values.images.resizer.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + - "--csi-address=$(ADDRESS)" + - "--timeout=180s" + - "--v=5" + - "--leader-election" + env: + - name: ADDRESS + value: /var/run/csi/csi.sock + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + {{- end }} + {{- end }} + {{- if hasKey .Values "authorization" }} + {{- if eq .Values.authorization.enabled true }} + - name: karavi-authorization-proxy + imagePullPolicy: {{ .Values.imagePullPolicy }} + image: {{ required "Must provide the authorization sidecar container image." .Values.images.authorization.image }} + env: + - name: PROXY_HOST + value: "{{ .Values.authorization.proxyHost }}" + - name: SKIP_CERTIFICATE_VALIDATION + value: "{{ .Values.authorization.skipCertificateValidation }}" + - name: PLUGIN_IDENTIFIER + value: powermax + - name: ACCESS_TOKEN + valueFrom: + secretKeyRef: + name: proxy-authz-tokens + key: access + - name: REFRESH_TOKEN + valueFrom: + secretKeyRef: + name: proxy-authz-tokens + key: refresh + volumeMounts: + - name: karavi-authorization-config + mountPath: /etc/karavi-authorization/config + - name: proxy-server-root-certificate + mountPath: /etc/karavi-authorization/root-certificates + - name: powermax-config-params + mountPath: /etc/karavi-authorization + {{- end }} + {{- end }} + {{- if hasKey .Values.controller "healthMonitor" }} + {{- if eq .Values.controller.healthMonitor.enabled true }} + - name: csi-external-health-monitor-controller + imagePullPolicy: {{ .Values.imagePullPolicy }} + image: {{ required "Must provide the CSI external health monitor controller image." .Values.images.healthmonitor.image }} + args: + - "--v=5" + - "--csi-address=$(ADDRESS)" + - "--leader-election" + - "--http-endpoint=:8080" + - "--enable-node-watcher=true" + - "--monitor-interval={{ .Values.controller.healthMonitor.interval | default "60s" }}" + - "--timeout=180s" + env: + - name: ADDRESS + value: /var/run/csi/csi.sock + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + {{- end }} + {{- end }} + - name: driver + image: {{ required "Must provide the PowerMax driver image repository." .Values.images.driver.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + command: [ "/csi-powermax.sh" ] + env: + {{- $_ := first .Values.global.storageArrays }} + {{- $arraysStr := "" }} + {{- range $i, $array := .Values.global.storageArrays }} + {{- $arraysStr = trim (cat $arraysStr $array.storageArrayId) }} + {{- end }} + - name: X_CSI_POWERMAX_DRIVER_NAME + {{- if eq .Values.customDriverName.enabled true}} + value: {{ required "Must provide a driver name" (printf "%s.%s.dellemc.com" .Release.Namespace .Values.customDriverName.value) }} + {{- else }} + value: csi-powermax.dellemc.com + {{- end }} + - name: CSI_ENDPOINT + value: /var/run/csi/csi.sock + - name: X_CSI_MODE + value: controller + - name: X_CSI_POWERMAX_USER + valueFrom: + secretKeyRef: + name: {{ .Values.global.defaultCredentialsSecret }} + key: username + - name: X_CSI_POWERMAX_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.global.defaultCredentialsSecret }} + key: password + - name: X_CSI_POWERMAX_CONFIG_PATH + value: /powermax-config-params/driver-config-params.yaml + - name: X_CSI_POWERMAX_ARRAY_CONFIG_PATH + value: /powermax-array-config/powermax-array-config.yaml + - name: X_CSI_POWERMAX_DEBUG + value: {{ .Values.powerMaxDebug | default "false" | lower | quote }} + - name: X_CSI_POWERMAX_SKIP_CERTIFICATE_VALIDATION + value: {{ .Values.skipCertificateValidation | default "true" | lower | quote }} + {{- if eq .Values.csireverseproxy.deployAsSidecar true }} + - name: X_CSI_POWERMAX_SIDECAR_PROXY_PORT + value: {{ .Values.csireverseproxy.port | quote }} + {{- else }} + - name: X_CSI_POWERMAX_PROXY_SERVICE_NAME + value: {{ .Release.Name }}-reverseproxy + {{- end }} + - name: X_CSI_K8S_CLUSTER_PREFIX + value: {{ required "Must provide a Cluster Prefix." .Values.clusterPrefix }} + - name: X_CSI_GRPC_MAX_THREADS + value: "50" + - name: SSL_CERT_DIR + value: /certs + - name: X_CSI_IG_NODENAME_TEMPLATE + value: {{ .Values.nodeNameTemplate | default "" }} + - name: X_CSI_IG_MODIFY_HOSTNAME + value: {{ .Values.modifyHostName | default "false" | lower | quote }} + - name: X_CSI_REPLICATION_CONTEXT_PREFIX + value: {{ .Values.replication.replicationContextPrefix | default "powermax"}} + - name: X_CSI_REPLICATION_PREFIX + value: {{ .Values.replication.replicationPrefix | default "replication.storage.dell.com"}} + - name: X_CSI_MIGRATION_PREFIX + value: {{ .Values.migration.migrationPrefix | default "migration.storage.dell.com"}} + - name: X_CSI_UNISPHERE_TIMEOUT + value: {{.Values.unisphereTimeout | default "5m"}} + {{- if hasKey .Values.controller "healthMonitor" }} + {{- if eq .Values.controller.healthMonitor.enabled true }} + - name: X_CSI_HEALTH_MONITOR_ENABLED + value: "{{ .Values.controller.healthMonitor.enabled }}" + {{- end }} + {{- end }} + - name: X_CSI_VSPHERE_ENABLED + value: "{{ .Values.vSphere.enabled }}" + {{- if eq .Values.vSphere.enabled true }} + - name: X_CSI_VSPHERE_PORTGROUP + value: {{ required "Must provide portgroup for vsphere" .Values.vSphere.fcPortGroup }} + - name: X_CSI_VSPHERE_HOSTNAME + value: {{ required "Must provide host group for vsphere" .Values.vSphere.fcHostName }} + - name: X_CSI_VCENTER_HOST + value: {{ required "Must provide host url for vsphere" .Values.vSphere.vCenterHost }} + - name: X_CSI_VCENTER_USERNAME + valueFrom: + secretKeyRef: + name: {{ .Values.vSphere.vCenterCredSecret }} + key: username + - name: X_CSI_VCENTER_PWD + valueFrom: + secretKeyRef: + name: {{ .Values.vSphere.vCenterCredSecret }} + key: password + {{- end }} + {{- if hasKey .Values "podmon" }} + - name: X_CSI_PODMON_ENABLED + value: "{{ .Values.podmon.enabled }}" + {{- if eq .Values.podmon.enabled true }} + {{- range $key, $value := .Values.podmon.controller.args }} + {{- if contains "--arrayConnectivityPollRate" $value }} + - name: X_CSI_PODMON_ARRAY_CONNECTIVITY_POLL_RATE + value: "{{ (split "=" $value)._1 }}" + {{- end }} + {{- end }} + {{- end }} + {{- end }} + - name: X_CSI_PODMON_API_PORT + value: "{{ .Values.podmon.podmonAPIPort }}" + - name: X_CSI_REVPROXY_TLS_CERT_DIR + value: /app/tls + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + - name: certs + mountPath: /certs + readOnly: true + - name: powermax-config-params + mountPath: /powermax-config-params + - name: powermax-array-config + mountPath: /powermax-array-config + - name: tls-secret + mountPath: /app/tls + {{- if eq .Values.csireverseproxy.deployAsSidecar true }} + - name: reverseproxy + image: {{ required "Must provided an image for reverseproxy container." .Values.images.csireverseproxy.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + env: + - name: X_CSI_REVPROXY_CONFIG_DIR + value: /etc/config/configmap + - name: X_CSI_REVPROXY_CONFIG_FILE_NAME + value: config.yaml + - name: X_CSI_REVRPOXY_IN_CLUSTER + value: "true" + - name: X_CSI_REVPROXY_TLS_CERT_DIR + value: /app/tls + - name: X_CSI_REVPROXY_WATCH_NAMESPACE + value: {{ .Release.Namespace }} + volumeMounts: + - name: configmap-volume + mountPath: /etc/config/configmap + - name: tls-secret + mountPath: /app/tls + - name: cert-dir + mountPath: /app/certs + {{- end }} + volumes: + - name: socket-dir + emptyDir: + - name: certs + secret: + secretName: {{ .Release.Name }}-certs + optional: true + - name: configmap-volume + configMap: + name: {{ .Release.Name }}-reverseproxy-config + optional: true + - name: tls-secret + secret: + secretName: {{ .Values.csireverseproxy.tlsSecret }} + - name: powermax-array-config + configMap: + name: {{ .Release.Name }}-array-config + - name: cert-dir + emptyDir: + - name: powermax-config-params + configMap: + name: {{ .Release.Name }}-config-params + {{- if hasKey .Values "authorization" }} + {{- if eq .Values.authorization.enabled true }} + - name: karavi-authorization-config + secret: + secretName: karavi-authorization-config + - name: proxy-server-root-certificate + secret: + secretName: proxy-server-root-certificate + {{ end }} + {{ end }} diff --git a/charts/dell/csi-powermax/2.13.0/templates/csidriver.yaml b/charts/dell/csi-powermax/2.13.0/templates/csidriver.yaml new file mode 100644 index 0000000000..88b7c7c7b3 --- /dev/null +++ b/charts/dell/csi-powermax/2.13.0/templates/csidriver.yaml @@ -0,0 +1,13 @@ +apiVersion: storage.k8s.io/v1 +kind: CSIDriver +metadata: + {{- if eq .Values.customDriverName.enabled true}} + name: {{ printf "%s-%s" .Release.Namespace .Values.customDriverName.value }} + {{- else }} + name: csi-powermax.dellemc.com + {{- end }} +spec: + podInfoOnMount: true + attachRequired: true + storageCapacity: {{ (include "csi-powermax.isStorageCapacitySupported" .) | default false }} + fsGroupPolicy: {{ .Values.fsGroupPolicy }} diff --git a/charts/dell/csi-powermax/2.13.0/templates/driver-config-params.yaml b/charts/dell/csi-powermax/2.13.0/templates/driver-config-params.yaml new file mode 100644 index 0000000000..12671a0d3b --- /dev/null +++ b/charts/dell/csi-powermax/2.13.0/templates/driver-config-params.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Release.Name }}-config-params + namespace: {{ .Release.Namespace }} +data: + driver-config-params.yaml: | + CSI_LOG_LEVEL: {{ .Values.global.logLevel | default "debug" }} + CSI_LOG_FORMAT: {{ .Values.global.logFormat | default "TEXT" }} diff --git a/charts/dell/csi-powermax/2.13.0/templates/node.yaml b/charts/dell/csi-powermax/2.13.0/templates/node.yaml new file mode 100644 index 0000000000..f753aec0ad --- /dev/null +++ b/charts/dell/csi-powermax/2.13.0/templates/node.yaml @@ -0,0 +1,517 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{.Release.Name}}-node + namespace: {{ .Release.Namespace }} +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + {{- if eq .Values.customDriverName.enabled true}} + name: {{ printf "%s-%s-node" .Release.Namespace .Values.customDriverName.value }} + {{- else }} + name: {{ .Release.Name }}-node + {{- end }} +rules: + - apiGroups: [""] + resources: ["nodes"] + verbs: ["get", "list", "watch", "update", "patch"] + - apiGroups: [ "" ] + resources: [ "persistentvolumes" ] + verbs: [ "create", "delete", "get", "list", "watch", "update" ] + - apiGroups: [ "" ] + resources: [ "persistentvolumesclaims" ] + verbs: [ "get", "list", "watch", "update" ] + - apiGroups: [ "" ] + resources: [ "events" ] + verbs: [ "get", "list", "watch", "create", "update", "patch" ] + - apiGroups: [ "storage.k8s.io" ] + resources: [ "volumeattachments" ] + verbs: [ "get", "list", "watch", "update" ] + - apiGroups: [ "storage.k8s.io" ] + resources: [ "storageclasses" ] + verbs: [ "get", "list", "watch" ] + {{- if eq .Values.openshift true }} + - apiGroups: ["security.openshift.io"] + resources: ["securitycontextconstraints"] + resourceNames: ["privileged"] + verbs: ["use"] + {{- end }} + #below for node rescan sidecar + {{- if eq .Values.migration.enabled true}} + - apiGroups: ["replication.storage.dell.com"] + resources: ["dellcsimigrationgroups"] + verbs: ["create", "delete", "get", "list", "patch", "update", "watch"] + - apiGroups: ["replication.storage.dell.com"] + resources: ["dellcsimigrationgroups/status"] + verbs: ["get", "patch", "update"] + - apiGroups: [""] + resources: ["configmaps"] + verbs: ["create", "delete", "get", "list", "watch", "update", "patch"] + - apiGroups: [ "" ] + resources: [ "events" ] + verbs: [ "list", "watch", "create", "update", "patch" ] + - apiGroups: [ "" ] + resources: [ "pods" ] + verbs: [ "get", "list", "watch", "update", "patch" ] + {{- end}} + #below for podmon + {{- if hasKey .Values "podmon" }} + {{- if eq .Values.podmon.enabled true }} + - apiGroups: [ "" ] + resources: [ "pods" ] + verbs: [ "get", "list", "watch", "update", "delete" ] + {{- end }} + {{- end }} +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + {{- if eq .Values.customDriverName.enabled true}} + name: {{ printf "%s-%s-node" .Release.Namespace .Values.customDriverName.value }} + {{- else }} + name: {{ .Release.Name }}-node + {{- end }} +subjects: + - kind: ServiceAccount + name: {{ .Release.Name }}-node + namespace: {{ .Release.Namespace }} +roleRef: + kind: ClusterRole + {{- if eq .Values.customDriverName.enabled true}} + name: {{ printf "%s-%s-node" .Release.Namespace .Values.customDriverName.value }} + {{- else }} + name: {{ .Release.Name }}-node + {{- end }} + apiGroup: rbac.authorization.k8s.io +--- +kind: DaemonSet +apiVersion: apps/v1 +metadata: + name: {{ .Release.Name }}-node + namespace: {{ .Release.Namespace }} + {{- if hasKey .Values "authorization" }} + {{- if eq .Values.authorization.enabled true }} + annotations: + com.dell.karavi-authorization-proxy: "true" + {{ end }} + {{ end }} +spec: + selector: + matchLabels: + app: {{ .Release.Name }}-node + template: + metadata: + annotations: + kubectl.kubernetes.io/default-container: "driver" + labels: + app: {{ .Release.Name }}-node + {{- if .Values.podmon.enabled }} + driver.dellemc.com: dell-storage + {{- end }} + spec: + serviceAccountName: {{ .Release.Name }}-node + {{ if .Values.node.nodeSelector }} + nodeSelector: + {{- toYaml .Values.node.nodeSelector | nindent 8 }} + {{ end }} + {{ if .Values.node.tolerations }} + tolerations: + {{- toYaml .Values.node.tolerations | nindent 6 }} + {{ end }} + hostIPC: true + hostNetwork: true + dnsPolicy: ClusterFirstWithHostNet + containers: + {{- $_ := first .Values.global.storageArrays }} + {{- $arraysStr := "" }} + {{- range $i, $array := .Values.global.storageArrays }} + {{- $arraysStr = trim (cat $arraysStr $array.storageArrayId) }} + {{- end }} + - name: driver + securityContext: + privileged: true + capabilities: + add: ["SYS_ADMIN"] + allowPrivilegeEscalation: true + image: {{ required "Must provide the PowerMax driver image repository." .Values.images.driver.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + command: [ "/csi-powermax.sh" ] + env: + - name: X_CSI_POWERMAX_DRIVER_NAME + {{- if eq .Values.customDriverName.enabled true }} + value: {{ required "Must provide a driver name" (printf "%s.%s.dellemc.com" .Release.Namespace .Values.customDriverName.value) }} + {{- else }} + value: csi-powermax.dellemc.com + {{- end }} + - name: CSI_ENDPOINT + value: unix://{{ .Values.kubeletConfigDir }}/plugins/powermax.emc.dell.com/csi_sock + - name: X_CSI_MODE + value: node + - name: X_CSI_MAX_VOLUMES_PER_NODE + value: "{{ .Values.maxPowerMaxVolumesPerNode }}" + - name: X_CSI_PRIVATE_MOUNT_DIR + value: "{{ .Values.kubeletConfigDir }}/plugins/powermax.emc.dell.com/disks" + {{- $managementServer := first .Values.global.managementServers }} + - name: X_CSI_POWERMAX_ENDPOINT + value: {{ required "Must provide a Unisphere HTTPS endpoint." $managementServer.endpoint }} + - name: X_CSI_POWERMAX_DEBUG + value: {{ .Values.powerMaxDebug | default "false" | lower | quote }} + - name: X_CSI_POWERMAX_SKIP_CERTIFICATE_VALIDATION + value: {{ .Values.skipCertificateValidation | default "true" | lower | quote }} + - name: X_CSI_K8S_CLUSTER_PREFIX + value: {{ required "Must provide a Cluster Prefix." .Values.clusterPrefix }} + - name: X_CSI_POWERMAX_USER + valueFrom: + secretKeyRef: + name: {{ .Values.global.defaultCredentialsSecret }} + key: username + - name: X_CSI_POWERMAX_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.global.defaultCredentialsSecret }} + key: password + - name: X_CSI_POWERMAX_NODENAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + {{- if eq .Values.enableCHAP true }} + - name: X_CSI_POWERMAX_ISCSI_ENABLE_CHAP + value: "true" + - name: X_CSI_POWERMAX_ISCSI_CHAP_USERNAME + value: "" + - name: X_CSI_POWERMAX_ISCSI_CHAP_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Release.Name }}-creds + key: chapsecret + {{- else }} + - name: X_CSI_POWERMAX_ISCSI_ENABLE_CHAP + value: "false" + {{- end }} + - name: X_CSI_POWERMAX_PROXY_SERVICE_NAME + value: {{ .Release.Name }}-reverseproxy + - name: X_CSI_NODE_CHROOT + value: {{ .Values.ISCSIChroot | default "/noderoot" }} + - name: X_CSI_GRPC_MAX_THREADS + value: "50" + - name: SSL_CERT_DIR + value: /certs + - name: X_CSI_POWERMAX_CONFIG_PATH + value: /powermax-config-params/driver-config-params.yaml + - name: X_CSI_POWERMAX_ARRAY_CONFIG_PATH + value: /powermax-array-config/powermax-array-config.yaml + - name: X_CSI_POWERMAX_TOPOLOGY_CONFIG_PATH + value: /node-topology-config/topologyConfig.yaml + - name: X_CSI_IG_NODENAME_TEMPLATE + value: {{ .Values.nodeNameTemplate | default "" }} + - name: X_CSI_IG_MODIFY_HOSTNAME + value: {{ .Values.modifyHostName | default "false" | lower | quote }} + {{- if hasKey .Values.node "healthMonitor" }} + {{- if eq .Values.node.healthMonitor.enabled true }} + - name: X_CSI_HEALTH_MONITOR_ENABLED + value: "{{ .Values.node.healthMonitor.enabled }}" + {{- end }} + {{- end }} + {{- if hasKey .Values.node "topologyControl" }} + {{- if eq .Values.node.topologyControl.enabled true }} + - name: X_CSI_TOPOLOGY_CONTROL_ENABLED + value: "{{ .Values.node.topologyControl.enabled }}" + {{- end }} + {{- end }} + - name: X_CSI_VSPHERE_ENABLED + value: "{{ .Values.vSphere.enabled }}" + {{- if eq .Values.vSphere.enabled true }} + - name: X_CSI_VSPHERE_PORTGROUP + value: {{ required "Must provide portgroup for vsphere" .Values.vSphere.fcPortGroup }} + - name: X_CSI_VSPHERE_HOSTNAME + value: {{ required "Must provide host group for vsphere" .Values.vSphere.fcHostName }} + - name: X_CSI_VCENTER_HOST + value: {{ required "Must provide hosr url for vsphere" .Values.vSphere.vCenterHost }} + - name: X_CSI_VCENTER_USERNAME + valueFrom: + secretKeyRef: + name: {{ .Values.vSphere.vCenterCredSecret }} + key: username + - name: X_CSI_VCENTER_PWD + valueFrom: + secretKeyRef: + name: {{ .Values.vSphere.vCenterCredSecret }} + key: password + {{- end }} + {{- if hasKey .Values "podmon" }} + - name: X_CSI_PODMON_ENABLED + value: "{{ .Values.podmon.enabled }}" + {{- if eq .Values.podmon.enabled true }} + {{- range $key, $value := .Values.podmon.node.args }} + {{- if contains "--arrayConnectivityPollRate" $value }} + - name: X_CSI_PODMON_ARRAY_CONNECTIVITY_POLL_RATE + value: "{{ (split "=" $value)._1 }}" + {{- end }} + {{- end }} + {{- end }} + {{- end }} + - name: X_CSI_PODMON_API_PORT + value: "{{ .Values.podmon.podmonAPIPort }}" + - name: X_CSI_REVPROXY_TLS_CERT_DIR + value: /app/tls + volumeMounts: + - name: driver-path + mountPath: {{ .Values.kubeletConfigDir }}/plugins/powermax.emc.dell.com + - name: volumedevices-path + mountPath: {{ .Values.kubeletConfigDir }}/plugins/kubernetes.io/csi/volumeDevices + mountPropagation: "Bidirectional" + - name: pods-path + mountPath: {{ .Values.kubeletConfigDir }}/pods + mountPropagation: "Bidirectional" + - name: dev + mountPath: /dev + - name: sys + mountPath: /sys + - name: noderoot + mountPath: {{ .Values.ISCSIChroot | default "/noderoot" }} + - name: certs + mountPath: /certs + readOnly: true + - name: dbus-socket + mountPath: /run/dbus/system_bus_socket + - name: powermax-config-params + mountPath: /powermax-config-params + - name: powermax-array-config + mountPath: /powermax-array-config + {{- if hasKey .Values.node "topologyControl" }} + {{- if eq .Values.node.topologyControl.enabled true }} + - name: node-topology-config + mountPath: /node-topology-config + {{- end }} + {{- end }} + - name: tls-secret + mountPath: /app/tls + - name: registrar + image: {{ required "Must provide the CSI node registrar container image." .Values.images.registrar.image }} + args: + - "--v=5" + - "--csi-address=$(ADDRESS)" + #- --mode=node-register + #- --driver-requires-attachment=true + #- --pod-info-mount-version=v1 + - --kubelet-registration-path={{ .Values.kubeletConfigDir }}/plugins/powermax.emc.dell.com/csi_sock + env: + - name: ADDRESS + value: /csi/csi_sock + - name: KUBE_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + volumeMounts: + - name: registration-dir + mountPath: /registration + - name: driver-path + mountPath: /csi + {{- if hasKey .Values "migration" }} + {{- if eq .Values.migration.enabled true }} + - name: csi-node-rescanner + securityContext: + privileged: true + capabilities: + add: [ "SYS_ADMIN" ] + allowPrivilegeEscalation: true + imagePullPolicy: {{ .Values.imagePullPolicy }} + image: {{ required "Must provide the node rescanner sidecar container image." .Values.images.noderescan.image }} + args: + - "--csi-address=$(ADDRESS)" + - "--retry-interval-start=1s" + - "--retry-interval-max=300s" + - "--timeout=300s" + - "--prefix={{ .Values.migration.migrationPrefix}}" + env: + - name: ADDRESS + value: /csi/csi_sock + - name: X_CSI_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + volumeMounts: + - name: registration-dir + mountPath: /registration + - name: driver-path + mountPath: /csi + - name: dev + mountPath: /dev + - name: sys + mountPath: /sys + - name: noderoot + mountPath: {{ .Values.ISCSIChroot | default "/noderoot" }} + - name: volumedevices-path + mountPath: {{ .Values.kubeletConfigDir }}/plugins/kubernetes.io/csi/volumeDevices + mountPropagation: "Bidirectional" + - name: pods-path + mountPath: {{ .Values.kubeletConfigDir }}/pods + mountPropagation: "Bidirectional" + {{ end }} + {{ end }} + {{- if hasKey .Values "authorization" }} + {{- if eq .Values.authorization.enabled true }} + - name: karavi-authorization-proxy + imagePullPolicy: {{ .Values.imagePullPolicy }} + image: {{ required "Must provide the authorization sidecar container image." .Values.images.authorization.image }} + env: + - name: PROXY_HOST + value: "{{ .Values.authorization.proxyHost }}" + - name: SKIP_CERTIFICATE_VALIDATION + value: "{{ .Values.authorization.skipCertificateValidation }}" + - name: PLUGIN_IDENTIFIER + value: powermax + - name: ACCESS_TOKEN + valueFrom: + secretKeyRef: + name: proxy-authz-tokens + key: access + - name: REFRESH_TOKEN + valueFrom: + secretKeyRef: + name: proxy-authz-tokens + key: refresh + volumeMounts: + - name: karavi-authorization-config + mountPath: /etc/karavi-authorization/config + - name: proxy-server-root-certificate + mountPath: /etc/karavi-authorization/root-certificates + - name: powermax-config-params + mountPath: /etc/karavi-authorization + {{ end }} + {{ end }} + {{- if hasKey .Values "podmon" }} + {{- if eq .Values.podmon.enabled true }} + - name: podmon + securityContext: + privileged: true + capabilities: + add: [ "SYS_ADMIN" ] + allowPrivilegeEscalation: true + image: {{ required "Must provide the podmon container image." .Values.images.podmon.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + {{- toYaml .Values.podmon.node.args | nindent 12 }} + env: + - name: KUBE_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + - name: X_CSI_PRIVATE_MOUNT_DIR + value: {{ .Values.kubeletConfigDir }} + - name: MY_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: MY_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: MY_POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + volumeMounts: + - name: kubelet-pods + mountPath: {{ .Values.kubeletConfigDir }}/pods + mountPropagation: "Bidirectional" + - name: driver-path + mountPath: {{ .Values.kubeletConfigDir }}/plugins/powermax.emc.dell.com + mountPropagation: "Bidirectional" + - name: csi-path + mountPath: {{ .Values.kubeletConfigDir }}/plugins/kubernetes.io/csi + mountPropagation: "Bidirectional" + - name: dev + mountPath: /dev + - name: usr-bin + mountPath: /usr-bin + - name: var-run + mountPath: /var/run + - name: powermax-config-params + mountPath: /powermax-config-params + {{- end }} + {{- end }} + volumes: + - name: registration-dir + hostPath: + path: {{ .Values.kubeletConfigDir }}/plugins_registry/ + type: DirectoryOrCreate + - name: driver-path + hostPath: + path: {{ .Values.kubeletConfigDir }}/plugins/powermax.emc.dell.com + type: DirectoryOrCreate + - name: volumedevices-path + hostPath: + path: {{ .Values.kubeletConfigDir }}/plugins/kubernetes.io/csi/volumeDevices + type: DirectoryOrCreate + - name: pods-path + hostPath: + path: {{ .Values.kubeletConfigDir }}/pods + type: Directory + - name: csi-path + hostPath: + path: {{ .Values.kubeletConfigDir }}/plugins/kubernetes.io/csi + - name: dev + hostPath: + path: /dev + type: Directory + - name: sys + hostPath: + path: /sys + type: Directory + - name: noderoot + hostPath: + path: / + type: Directory + - name: dbus-socket + hostPath: + path: /run/dbus/system_bus_socket + type: Socket + - name: powermax-config-params + configMap: + name: {{ .Release.Name }}-config-params + - name: powermax-array-config + configMap: + name: {{ .Release.Name }}-array-config + - name: certs + secret: + secretName: {{ .Release.Name }}-certs + optional: true + {{- if hasKey .Values.node "topologyControl" }} + {{- if eq .Values.node.topologyControl.enabled true }} + - name: node-topology-config + configMap: + name: node-topology-config + {{- end }} + {{- end }} + {{- if hasKey .Values "authorization" }} + {{- if eq .Values.authorization.enabled true }} + - name: karavi-authorization-config + secret: + secretName: karavi-authorization-config + - name: proxy-server-root-certificate + secret: + secretName: proxy-server-root-certificate + {{ end }} + {{ end }} + - name: usr-bin + hostPath: + path: /usr/bin + type: Directory + - name: kubelet-pods + hostPath: + path: /var/lib/kubelet/pods + type: Directory + - name: var-run + hostPath: + path: /var/run + type: Directory + - name: tls-secret + secret: + secretName: {{ .Values.csireverseproxy.tlsSecret }} diff --git a/charts/dell/csi-powermax/2.13.0/templates/powermax-array-config.yaml b/charts/dell/csi-powermax/2.13.0/templates/powermax-array-config.yaml new file mode 100644 index 0000000000..12904f5b77 --- /dev/null +++ b/charts/dell/csi-powermax/2.13.0/templates/powermax-array-config.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Release.Name }}-array-config + namespace: {{ .Release.Namespace }} +data: + powermax-array-config.yaml: | + X_CSI_POWERMAX_PORTGROUPS: {{ .Values.global.portGroups | toYaml | default "" }} + X_CSI_TRANSPORT_PROTOCOL: {{ .Values.global.transportProtocol | toYaml | default "" }} + {{- $_ := first .Values.global.storageArrays }} + {{- range $index, $value := .Values.global.storageArrays }} + X_CSI_POWERMAX_ENDPOINT: {{ $value.endpoint | toYaml | default "" }} + {{- end }} + X_CSI_MANAGED_ARRAYS: {{ .Values.global.managedArrays | toYaml | default "" }} \ No newline at end of file diff --git a/charts/dell/csi-powermax/2.13.0/values.yaml b/charts/dell/csi-powermax/2.13.0/values.yaml new file mode 100644 index 0000000000..72fe6d0dca --- /dev/null +++ b/charts/dell/csi-powermax/2.13.0/values.yaml @@ -0,0 +1,510 @@ +--- +global: + # CSI driver log level + # Allowed values: "error", "warn"/"warning", "info", "debug" + # Default value: "info" + logLevel: "info" + + # CSI driver log format + # Allowed values: "TEXT" or "JSON" + # Default value: "TEXT" + logFormat: "TEXT" + ########################## + # PLATFORM ATTRIBUTES + ########################## + # The CSI PowerMax ReverseProxy section to fill out the required configuration + # Please refer to the doc website about a + # detailed explanation of each configuration parameter + # X_CSI_MANAGED_ARRAYS: Serial ID of the arrays that will be used for provisioning + # Default value: None + # Examples: "000000000001", "000000000002" + managedArrays: "000000000001,000000000002" + # defaultCredentialsSecret + defaultCredentialsSecret: powermax-creds + # portGroups: Define the set of existing port groups that the driver will use. + # It is a comma separated list of portgroup names. + # Required only in case of iSCSI port groups + # Allowed values: iSCSI Port Group names + # Default value: None + # Examples: "pg1, pg2, pg3", "pg1" + portGroups: + # "transportProtocol" can be "FC" or "FIBRE" for fibrechannel, + # "ISCSI" for iSCSI, or "" for autoselection. + # Allowed values: + # "FC" - Fiber Channel protocol + # "FIBER" - Fiber Channel protocol + # "ISCSI" - iSCSI protocol + # "" - Automatic selection of transport protocol + # Default value: "" + transportProtocol: "" + storageArrays: + - storageArrayId: "000000000001" + endpoint: https://primary-1.unisphe.re:8443 + backupEndpoint: https://backup-1.unisphe.re:8443 + # - storageArrayId: "000000000002" + # endpoint: https://primary-2.unisphe.re:8443 + # backupEndpoint: https://backup-2.unisphe.re:8443 + managementServers: + - endpoint: https://primary-1.unisphe.re:8443 + credentialsSecret: primary-1-secret + skipCertificateValidation: true + certSecret: primary-cert + limits: + maxActiveRead: 5 + maxActiveWrite: 4 + maxOutStandingRead: 50 + maxOutStandingWrite: 50 + - endpoint: https://backup-1.unisphe.re:8443 + credentialsSecret: backup-1-secret + skipCertificateValidation: true +# - endpoint: https://primary-2.unisphe.re:8443 +# credentialsSecret: primary-2-secret +# skipCertificateValidation: true +# certSecret: no-secret +# - endpoint: https://backup-2.unisphe.re:8443 +# credentialsSecret: backup-2-secret +# skipCertificateValidation: true + +# Current version of the driver +# Don't modify this value as this value will be used by the install script +version: "v2.13.0" + +# "images" defines every container images used for the driver and its sidecars. +# To use your own images, or a private registry, change the values here. +images: + # "driver" defines the container image, used for the driver container. + driver: + image: quay.io/dell/container-storage-modules/csi-powermax:v2.13.0 + csireverseproxy: + image: quay.io/dell/container-storage-modules/csipowermax-reverseproxy:v2.12.0 + # CSI sidecars + attacher: + image: registry.k8s.io/sig-storage/csi-attacher:v4.8.0 + provisioner: + image: registry.k8s.io/sig-storage/csi-provisioner:v5.1.0 + snapshotter: + image: registry.k8s.io/sig-storage/csi-snapshotter:v8.2.0 + resizer: + image: registry.k8s.io/sig-storage/csi-resizer:v1.13.1 + registrar: + image: registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.13.0 + healthmonitor: + image: registry.k8s.io/sig-storage/csi-external-health-monitor-controller:v0.14.0 + # CSM sidecars + replication: + image: quay.io/dell/container-storage-modules/dell-csi-replicator:v1.11.0 + authorization: + image: quay.io/dell/container-storage-modules/csm-authorization-sidecar:v2.1.0 + migration: + image: quay.io/dell/container-storage-modules/dell-csi-migrator:v1.7.0 + podmon: + image: quay.io/dell/container-storage-modules/podmon:v1.12.0 + # Node rescan sidecar does a rescan on nodes for identifying new paths + noderescan: + image: quay.io/dell/container-storage-modules/dell-csi-node-rescanner:v1.6.0 +## K8S/DRIVER ATTRIBUTES +######################## +# customDriverName: If enabled, sets the driver name to the +# value provided to its value field with namespace prefixed to it. + +# e.g, namespace.csi-powermax +# Default value: None +# Examples: "namespace.dellemc-array", "namespace.powermaxarray" +customDriverName: + value: csi-powermax + # Allowed values: + # "true" - Custom driver name is enabled + # "false" - Custom driver name is disabled + # Default value: "false" + enabled: false + +# defaultFsType: Sets the default FS type which will be used +# for mount volumes if FsType is not specified in the storage class +# Allowed values: +# "ext4" - EXT4 File system +# "xfs" - XFS File system +# Default value: "ext4" +defaultFsType: ext4 + +# imagePullPolicy: Policy to determine if the image should be pulled prior to starting the container. +# Allowed values: +# Always: Always pull the image. +# IfNotPresent: Only pull the image if it does not already exist on the node. +# Never: Never pull the image. +# Default value: None +imagePullPolicy: IfNotPresent + +# Specify kubelet config dir path. +# Ensure that the config.yaml file is present at this path. +# Default value: /var/lib/kubelet +kubeletConfigDir: /var/lib/kubelet + +# fsGroupPolicy: Defines if the underlying volume supports changing ownership and permission of the volume before being mounted. +# Allowed values: +# ReadWriteOnceWithFSType: supports volume ownership and permissions change only if the fsType is defined +# and the volume's accessModes contains ReadWriteOnce. +# File: kubernetes may use fsGroup to change permissions and ownership of the volume +# to match user requested fsGroup in the pod's security policy regardless of fstype or access mode. +# None: volumes will be mounted with no modifications. +# Default value: ReadWriteOnceWithFSType +fsGroupPolicy: ReadWriteOnceWithFSType + +# maxPowermaxVolumesPerNode: Specify default value for maximum number of volumes that controller can publish to the node. +# If value is zero CO SHALL decide how many volumes of this type can be published by the controller to the node. +# This limit is applicable to all the nodes in the cluster for which node label 'max-Powermax-volumes-per-node' is not set. +# Allowed values: n, where n >= 0 +# Default value: 0 +maxPowerMaxVolumesPerNode: 0 + +# controller: configure controller specific parameters +controller: + # controllerCount: Define the number of PowerMax controller nodes + # to deploy to the Kubernetes release + # Allowed values: n, where n > 0 + # Default value: None + controllerCount: 2 + + # volumeNamePrefix: Define a prefix that is prepended to volumes. + # THIS MUST BE ALL LOWER CASE. + # Default value: csivol + # Examples: "volumes", "vol" + volumeNamePrefix: csivol + + snapshot: + # enabled: Enable/Disable volume snapshot feature + # Allowed values: + # true: enable volume snapshot feature(install snapshotter sidecar) + # false: disable volume snapshot feature(do not install snapshotter sidecar) + # Default value: None + enabled: true + # snapNamePrefix: Define a prefix that is prepended to snapshots. + # THIS MUST BE ALL LOWER CASE. + # Default value: csi-snap + # Examples: "snap", "snapshot" + snapNamePrefix: csi-snap + + resizer: + # enabled: Enable/Disable volume expansion feature + # Allowed values: + # true: enable volume expansion feature(install resizer sidecar) + # false: disable volume snapshot feature(do not install resizer sidecar) + # Default value: None + enabled: true + + # nodeSelector: Define node selection constraints for controller pods. + # For the pod to be eligible to run on a node, the node must have each + # of the indicated key-value pairs as labels. + # Leave as blank to consider all nodes + # Allowed values: map of key-value pairs + # Default value: None + # Examples: + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/master taint + # node-role.kubernetes.io/master + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/control-plane taint + # node-role.kubernetes.io/control-plane + nodeSelector: + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/master taint + # node-role.kubernetes.io/master: "" + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/control-plane taint + # node-role.kubernetes.io/control-plane: "" + + # tolerations: Define tolerations that would be applied to controller deployment + # Leave as blank to install controller on worker nodes + # Allowed values: map of key-value pairs + # Default value: None + tolerations: + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/master taint + # - key: "node-role.kubernetes.io/master" + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/control-plane taint + # - key: "node-role.kubernetes.io/control-plane" + # operator: "Exists" + # effect: "NoSchedule" + + # health monitor showcase the volume usage and volume condition + healthMonitor: + # enabled: Enable/Disable health monitor of CSI volumes- volume status, volume condition + # Allowed values: + # true: enable checking of health condition of CSI volumes + # false: disable checking of health condition of CSI volumes + # Default value: None + enabled: false + + # interval: Interval of monitoring volume health condition + # Allowed values: Number followed by unit of time (s,m,h) + # Default value: 60s + interval: 60s + +# node: configure node specific parameters +node: + # nodeSelector: Define node selection constraints for node pods. + # For the pod to be eligible to run on a node, the node must have each + # of the indicated key-value pairs as labels. + # Leave as blank to consider all nodes + # Allowed values: map of key-value pairs + # Default value: None + # Examples: + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/master taint + # node-role.kubernetes.io/master + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/control-plane taint + # node-role.kubernetes.io/control-plane + nodeSelector: + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/master taint + # node-role.kubernetes.io/master: "" + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/control-plane taint + # node-role.kubernetes.io/control-plane: "" + + # tolerations: Define tolerations that would be applied to node daemonset + # Add/Remove tolerations as per requirement + # Leave as blank if you wish to not apply any tolerations + # Allowed values: map of key-value pairs + # Default value: None + tolerations: + - key: "node.kubernetes.io/memory-pressure" + operator: "Exists" + effect: "NoExecute" + - key: "node.kubernetes.io/disk-pressure" + operator: "Exists" + effect: "NoExecute" + - key: "node.kubernetes.io/network-unavailable" + operator: "Exists" + effect: "NoExecute" + + # health monitor showcase the volume usage and volume condition + healthMonitor: + # enabled: Enable/Disable health monitor of CSI volumes- volume usage, volume condition + # Allowed values: + # true: enable checking of health condition of CSI volumes + # false: disable checking of health condition of CSI volumes + # Default value: false + enabled: false + + # Topology control provides a way to filter topology keys + # Please refer to the doc website about a detailed explanation on its configuration and usage + topologyControl: + # enabled: Enable/Disable Topology Control + # Allowed values: + # true: enable the filtration based on config map + # false: disable the filtration based on config map + # Default value: false + enabled: false + +# enableCHAP: Determine if the driver is going to configure +# ISCSI node databases on the nodes with the CHAP credentials +# If enabled, the CHAP secret must be provided in the credentials secret +# and set to the key "chapsecret" +# Allowed values: +# "true" - CHAP is enabled +# "false" - CHAP is disabled +# Default value: "false" +enableCHAP: false + +# Use of this param is depreciated; +# setting this to any value will have no effect. +# "unisphere" defines the Unisphere endpoint, +# with full URL, typically leveraging HTTPS. +# This should include the port number as well (the default is 8443) +# You must set this for your Unisphere instance. +# unisphere: https://127.0.0.1:8443 + +# csireverseproxy: Refers to the subchart csireverseproxy +csireverseproxy: + # "tlsSecret" defines the TLS secret that is created with certificate + # and its associated key + # Default value: None + # Example: "tls-secret" + tlsSecret: csirevproxy-tls-secret + # Set enabled to true if you want to deploy csireverseproxy as sidecar + # Allowed values: + # "true" - CSI reverse proxy will be deployed as a sidecar + # "false" - CSI reverse proxy will be deployed along with driver + # Default value: "true" + deployAsSidecar: true + # Port number for csireverseproxy to listen + # Default value: None + # Examples: "1111", "8080" + port: 2222 + # Auto-create TLS certificate for csi-reverseproxy + certManager: + # Set selfSignedCert to use a self-signed certificate + # Default value: true + selfSignedCert: true + # certificateFile has tls.key content in encoded format + # Allowed Values: + # - encoded base64 value of tls.crt: cat tls.crt | base64 + # - comment the param, if selfsigned should be used + certificateFile: tls.crt.encoded64 + # privateKeyFile has tls.key content in encoded format + # Allowed Values: + # - encoded base64 value of tls.key: cat tls.key | base64 + # - comment the param, if selfsigned should be used + privateKeyFile: tls.key.encoded64 +# clusterPrefix: Define a prefix that is appended onto +# all resources created in the Array +# This should be unique per K8s/CSI deployment +# maximum length of this value is 3 characters +# Default value: None +# Examples: "XYZ" +clusterPrefix: ABC + +# "skipCertificateValidation" determines if driver is going to skip verification +# of TLS certificates while connecting to Unisphere RESTAPI interface +# If it is set to false, +# then a secret powermax-certs has to be created with a X.509 certificate of CA +# which signed the Unisphere certificate +# Allowed values: +# "true" - TLS certificates verification will be skipped +# "false" - TLS certificates will be verified +# Default value: "true" +skipCertificateValidation: "true" + +# "powerMaxDebug" enables low level and http traffic logging +# between the CSI driver and Unisphere. +# Do not enable this unless asked to do so by the support team. +# Allowed values: +# "true" - Traffic between the CSI driver and Unisphere is logged +# "false" - Traffic between the CSI driver and Unisphere will not be logged +# Default value: "false" +powerMaxDebug: "false" + +# nodeNameTemplate: Provide a template for the CSI driver to use +# while creating the Host/IG on the array for the nodes in the cluster. +# It is of the format a-b-c-%foo%-xyz +# where foo will be replaced by host name of each node in the cluster. +# For e.g. - If a node in the cluster has a hostname - worker1 +# then the host name with the above template would be a-b-c-worker1-xyz +# Default value: "" +# Examples: "a-b-c-worker1-xyz" , "a-b-c-workernode-xyz" +nodeNameTemplate: "" + +# modifyHostName: Change any existing host names. +# When nodenametemplate is set, +# it changes the name to the specified format +# else it uses driver default host name format. +# Allowed values: +# "true" - Host name will be modified +# "false" - Driver default host name format will be used +# Default value: "false" +modifyHostName: "false" + +# openshift: Define that the installation +# is being done on a Red Hat OpenShift cluster in the Helm Chart +# Don't modify this value as this value is overridden by the install script +openshift: false + +# CSM module attributes +# Set this to true to enable replication +# Replication CRDs must be installed before installing driver +# Allowed values: +# "true" - replication is enabled +# "false" - replication is disabled +# Default value: "false" +replication: + enabled: false + # replicationContextPrefix enables side cars to read + # required information from the volume context + # Default value: "powermax" + # Examples: "powermax-replication", "replication" + replicationContextPrefix: "powermax" + # replicationPrefix: Determine if replication is enabled + # Default value: "replication.storage.dell.com" + # Examples: "replication.storage.dell.com", "rdf.storage.dell.com" + replicationPrefix: "replication.storage.dell.com" + +# CSM module attributes +# Set this to true to enable migration +# Allowed values: +# "true" - migration is enabled +# "false" - migration is disabled +# Default value: "false" +migration: + enabled: false + # migrationPrefix: Determine if migration is enabled + # Default value: "migration.storage.dell.com" + # Examples: "migration.storage.dell.com" + migrationPrefix: "migration.storage.dell.com" + +# CSM module attributes +# authorization: enable csm-authorization for RBAC +# Deploy and configure authorization before installing driver +# Allowed values: +# "true" - authorization is enabled +# "false" - authorization is disabled +# Default value: "false" +authorization: + enabled: false + # proxyHost: hostname of the csm-authorization server + # Default value: None + proxyHost: + # skipCertificateValidation: certificate validation of the csm-authorization server + # Allowed Values: + # "true" - TLS certificate verification will be skipped + # "false" - TLS certificate will be verified + # Default value: "true" + skipCertificateValidation: true + +# Storage Capacity Tracking +# Note: Capacity tracking is supported in kubernetes v1.24 and above, this feature will be automatically disabled in older versions. +storageCapacity: + # enabled : Enable/Disable storage capacity tracking + # Allowed values: + # true: enable storage capacity tracking + # false: disable storage capacity tracking + # Default value: true + enabled: true + # pollInterval : Configure how often external-provisioner polls the driver to detect changed capacity + # Allowed values: 1m,2m,3m,...,10m,...,60m etc + # Default value: 5m + pollInterval: 5m + +# VMware/vSphere virtualization support +# set enable to true, if you to enable VMware virtualized environment support via RDM +# Allowed Values: +# "true" - vSphere volumes are enabled +# "false" - vSphere volumes are disabled +# Default value: "false" +vSphere: + enabled: false + # fcPortGroup: an existing portGroup that driver will use for vSphere + # recommended format: csi-x-VC-PG, x can be anything of user choice + fcPortGroup: "csi-vsphere-VC-PG" + # fcHostName: an existing host(initiator group) that driver will use for vSphere + # this host should contain initiators from all the ESXs/ESXi host + # where the cluster is deployed + # recommended format: csi-x-VC-HN, x can be anything of user choice + fcHostName: "csi-vsphere-VC-HN" + # vCenterHost: URL/endpoint of the vCenter where all the ESX are present + vCenterHost: "00.000.000.00" + # vCenterCredSecret: secret name for the vCenter credentials + vCenterCredSecret: vcenter-creds + +# Enable this feature only after contact support for additional information +podmon: + # podmonAPIPort: Defines the port to be used within the kubernetes cluster + # Allowed values: + # Any valid and free port. + # Default value: 8083 + podmonAPIPort: 8083 + enabled: false + controller: + args: + - "--csisock=unix:/var/run/csi/csi.sock" + - "--labelvalue=csi-powermax" + - "--arrayConnectivityPollRate=60" + - "--driverPath=csi-powermax.dellemc.com" + - "--mode=controller" + - "--skipArrayConnectionValidation=false" + - "--driver-config-params=/powermax-config-params/driver-config-params.yaml" + - "--driverPodLabelValue=dell-storage" + - "--ignoreVolumelessPods=false" + + node: + args: + - "--csisock=unix:/var/lib/kubelet/plugins/powermax.emc.dell.com/csi_sock" + - "--labelvalue=csi-powermax" + - "--arrayConnectivityPollRate=60" + - "--driverPath=csi-powermax.dellemc.com" + - "--mode=node" + - "--leaderelection=false" + - "--driver-config-params=/powermax-config-params/driver-config-params.yaml" + - "--driverPodLabelValue=dell-storage" + - "--ignoreVolumelessPods=false" diff --git a/charts/dell/csi-powerstore/2.13.0/Chart.yaml b/charts/dell/csi-powerstore/2.13.0/Chart.yaml new file mode 100644 index 0000000000..d7af9e5ae1 --- /dev/null +++ b/charts/dell/csi-powerstore/2.13.0/Chart.yaml @@ -0,0 +1,23 @@ +annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Dell CSI PowerStore + catalog.cattle.io/kube-version: '>= 1.24.0' + catalog.cattle.io/release-name: powerstore +apiVersion: v2 +appVersion: 2.13.0 +description: 'PowerStore CSI (Container Storage Interface) driver Kubernetes integration. + This chart includes everything required to provision via CSI as well as a PowerStore + StorageClass. ' +home: https://github.com/dell/csi-powerstore +icon: file://assets/icons/csi-powerstore.png +keywords: +- csi +- storage +kubeVersion: '>= 1.24.0' +maintainers: +- name: DellEMC +name: csi-powerstore +sources: +- https://github.com/dell/csi-powerstore +type: application +version: 2.13.0 diff --git a/charts/dell/csi-powerstore/2.13.0/app-readme.md b/charts/dell/csi-powerstore/2.13.0/app-readme.md new file mode 100644 index 0000000000..64441099be --- /dev/null +++ b/charts/dell/csi-powerstore/2.13.0/app-readme.md @@ -0,0 +1,92 @@ +# CSI Driver for Dell PowerStore Helm chart + +The [CSI Driver for Dell PowerStore](https://github.com/dell/csi-powerstore) is part of the CSM (Container Storage Modules) open-source suite of Kubernetes storage enablers for Dell EMC products. CSI Driver for PowerStore is a Container Storage Interface (CSI) driver that provides support for provisioning persistent storage using Dell EMC PowerStore storage array. + +## Prerequisites + +- Kubernetes version >= 1.23 (see [supported version](https://dell.github.io/csm-docs/docs/csidriver/#features-and-capabilities)) +- Helm 3 +- If you plan to use either the Fibre Channel or iSCSI or NVMe/TCP or NVMe/FC protocol, refer to either _Fibre Channel requirements_ or _Set up the iSCSI Initiator_ or _Set up the NVMe Initiator_ sections below. You can use NFS volumes without FC or iSCSI or NVMe/TCP or NVMe/FC configuration. +> You can use either the Fibre Channel or iSCSI or NVMe/TCP or NVMe/FC protocol, but you do not need all the four. + +> If you want to use preconfigured iSCSI/FC hosts be sure to check that they are not part of any host group +- Linux native multipathing requirements +- Mount propagation is enabled on container runtime that is being used +- If using Snapshot feature, satisfy all Volume Snapshot requirements +- Nonsecure registries are defined in Docker or other container runtimes, for CSI drivers that are hosted in a non-secure location. +- You can access your cluster with kubectl and helm. +- Ensure that your nodes support mounting NFS volumes. +- Install the Volume Snapshot CRDs by referring to [this](https://dell.github.io/csm-docs/docs/csidriver/installation/helm/powerstore/#optional-volume-snapshot-requirements) page. + +> Refer [this](https://dell.github.io/csm-docs/docs/csidriver/installation/helm/powerstore/#prerequisites) for setting up the prerequisites. + +## Optional Features +- [Volume Snapshot](https://dell.github.io/csm-docs/docs/csidriver/installation/helm/powerstore/#optional-volume-snapshot-requirements) +- [Volume Health Monitoring](https://dell.github.io/csm-docs/docs/csidriver/installation/helm/powerstore/#volume-health-monitoring) +- [Replication](https://dell.github.io/csm-docs/docs/csidriver/installation/helm/powerstore/#optional-replication-feature-requirements) + +## Install the Driver +**Steps** +1. Create a namespace where you want to install the driver (e.g. "csi-powerstore"). You can choose any name for the namespace, but make sure to align to the same namespace during the whole installation. +2. Create a secret named "powerstore-config" in the namespace created above. Sample [secret.yaml](https://github.com/dell/csi-powerstore/blob/main/samples/secret/secret.yaml). + >Secret must be of type opaque. +3. Create storage classes using ones from [samples](https://github.com/dell/csi-powerstore/tree/main/samples/storageclass) folder as an example. + > If you do not specify `arrayID` parameter in the storage class then the array that was specified as the default would be used for provisioning volumes. +4. Install the chart with the name "powerstore". The value.yaml file used during installation can be found [here](https://github.com/dell/csi-powerstore/blob/main/helm/csi-powerstore/values.yaml) + +The following table lists the configurable parameters of the chart and their default values. + +| Parameter | Description | Required | Default | +|-------------------------------------|-----------------------------------------------------------------------------------------------------------|----------|----------------------------| +| logLevel | Defines CSI driver log level | No | "debug" | +| logFormat | Defines CSI driver log format | No | "JSON" | +| externalAccess | Defines additional entries for hostAccess of NFS volumes, single IP address and subnet are valid entries | No | " " | +| kubeletConfigDir | Defines kubelet config path for cluster | Yes | "/var/lib/kubelet" | +| imagePullPolicy | Policy to determine if the image should be pulled prior to starting the container. | Yes | "IfNotPresent" | +| nfsAcls | Defines permissions - POSIX mode bits or NFSv4 ACLs, to be set on NFS target mount directory. | No | "0777" | +| connection.enableCHAP | Defines whether the driver should use CHAP for iSCSI connections or not | No | False | +| controller.controllerCount | Defines number of replicas of controller deployment | Yes | 2 | +| controller.volumeNamePrefix | Defines the string added to each volume that the CSI driver creates | No | "csivol" | +| controller.snapshot.enabled | Allows to enable/disable snapshotter sidecar with driver installation for snapshot feature | No | "true" | +| controller.snapshot.snapNamePrefix | Defines prefix to apply to the names of a created snapshots | No | "csisnap" | +| controller.resizer.enabled | Allows to enable/disable resizer sidecar with driver installation for volume expansion feature | No | "true" | +| controller.healthMonitor.enabled | Allows to enable/disable volume health monitor | No | false | +| controller.healthMonitor.interval | Interval of monitoring volume health condition | No | 60s | +| controller.nodeSelector | Defines what nodes would be selected for pods of controller deployment | Yes | " " | +| controller.tolerations | Defines toleration that would be applied to controller deployment | Yes | " " | +| node.nodeNamePrefix | Defines the string added to each node that the CSI driver registers | No | "csi-node" | +| node.nodeIDPath | Defines a path to file with a unique identifier identifying the node in the Kubernetes cluster | No | "/etc/machine-id" | +| node.healthMonitor.enabled | Allows to enable/disable volume health monitor | No | false | +| node.nodeSelector | Defines what nodes would be selected for pods of node daemonset | Yes | " " | +| node.tolerations | Defines toleration that would be applied to node daemonset | Yes | " " | +| fsGroupPolicy | Defines which FS Group policy mode to be used, Supported modes `None, File and ReadWriteOnceWithFSType` | No | "ReadWriteOnceWithFSType" | +| controller.vgsnapshot.enabled | To enable or disable the volume group snapshot feature | No | "true" | +| images.driverRepository | To use an image from custom repository | No | dockerhub | +| version | To use any driver version | No | Latest driver version | +| allowAutoRoundOffFilesystemSize | Allows the controller to round off filesystem to 3Gi which is the minimum supported value | No | false | +| storageCapacity.enabled | Enable/Disable storage capacity tracking | No | true | +| storageCapacity.pollInterval | Configure how often the driver checks for changed capacity | No | 5m | + +*NOTE:* +- By default, the driver scans available SCSI adapters and tries to register them with the storage array under the SCSI hostname using `node.nodeNamePrefix` and the ID read from the file pointed to by `node.nodeIDPath`. If an adapter is already registered with the storage under a different hostname, the adapter is not used by the driver. +- A hostname the driver uses for registration of adapters is in the form `--`. By default, these are csi-node and the machine ID read from the file `/etc/machine-id`. +- To customize the hostname, for example if you want to make them more user friendly, adjust nodeIDPath and nodeNamePrefix accordingly. For example, you can set `nodeNamePrefix` to `k8s` and `nodeIDPath` to `/etc/hostname` to produce names such as `k8s-worker1-192.168.1.2`. +- (Optional) Enable additional Mount Options - A user is able to specify additional mount options as needed for the driver. + - Mount options are specified in storageclass yaml under _mountOptions_. + - *WARNING*: Before utilizing mount options, you must first be fully aware of the potential impact and understand your environment's requirements for the specified option. + +## Support + +The CSI Driver for Dell PowerStore is fully supported by DELL. + +For all your support needs or to follow the latest ongoing discussions and updates, join our Slack group. Click [Here](http://del.ly/Slack_request) to request your invite. + +You can also interact with us on [GitHub](https://github.com/dell/csm) by creating a [GitHub Issue](https://github.com/dell/csm/issues). + +## Contributing + +We value all feedback and contributions. If you find any issues or want to contribute, please feel free to open an issue or file a PR. More details in [Contribution Guidelines](https://dell.github.io/csm-docs/docs/references/contributionguidelines/). + +## License + +This is open source software licensed using the Apache License 2.0. Please see [LICENSE](https://github.com/dell/csi-powerstore/blob/main/licenses/Apache.txt) for details. diff --git a/charts/dell/csi-powerstore/2.13.0/templates/_helpers.tpl b/charts/dell/csi-powerstore/2.13.0/templates/_helpers.tpl new file mode 100644 index 0000000000..cdbe7adac9 --- /dev/null +++ b/charts/dell/csi-powerstore/2.13.0/templates/_helpers.tpl @@ -0,0 +1,10 @@ +{{/* +Return true if storage capacity tracking is enabled and is supported based on k8s version +*/}} +{{- define "csi-powerstore.isStorageCapacitySupported" -}} +{{- if eq .Values.storageCapacity.enabled true -}} + {{- if and (eq .Capabilities.KubeVersion.Major "1") (ge (trimSuffix "+" .Capabilities.KubeVersion.Minor) "24") -}} + {{- true -}} + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/dell/csi-powerstore/2.13.0/templates/controller.yaml b/charts/dell/csi-powerstore/2.13.0/templates/controller.yaml new file mode 100644 index 0000000000..ef635339c0 --- /dev/null +++ b/charts/dell/csi-powerstore/2.13.0/templates/controller.yaml @@ -0,0 +1,457 @@ +# +# +# Copyright © 2020-2023 Dell Inc. or its subsidiaries. All Rights Reserved. +# +# 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: {{ .Release.Name }}-controller + namespace: {{ .Release.Namespace }} + +--- + +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ .Release.Name }}-controller +rules: + - apiGroups: ["coordination.k8s.io"] + resources: ["leases"] + verbs: ["get", "watch", "list", "delete", "update", "create"] + - apiGroups: [""] + resources: ["events"] + verbs: ["list", "watch", "create", "update", "patch"] + - apiGroups: [""] + resources: ["nodes"] + {{- if hasKey .Values "podmon" }} + {{- if eq .Values.podmon.enabled true }} + verbs: ["get", "list", "watch", "patch"] + {{- else }} + verbs: ["get", "list", "watch"] + {{- end }} + {{- end }} + - apiGroups: [""] + resources: ["persistentvolumes"] + verbs: ["get", "list", "watch", "create", "delete", "update", "patch"] + - apiGroups: [""] + resources: ["persistentvolumeclaims"] + verbs: ["get", "list", "watch", "update"] + - apiGroups: ["storage.k8s.io"] + resources: ["storageclasses"] + verbs: ["get", "list", "watch"] + - apiGroups: ["storage.k8s.io"] + resources: ["volumeattachments"] + {{- if hasKey .Values "podmon" }} + {{- if eq .Values.podmon.enabled true }} + verbs: ["get", "list", "watch", "update", "patch", "delete"] + {{- else }} + verbs: ["get", "list", "watch", "update", "patch"] + {{- end }} + {{- end }} + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "list"] + {{- if hasKey .Values.controller "vgsnapshot" }} + {{- if eq .Values.controller.vgsnapshot.enabled true }} + - apiGroups: ["volumegroup.storage.dell.com"] + resources: ["dellcsivolumegroupsnapshots","dellcsivolumegroupsnapshots/status"] + verbs: ["create", "list", "watch", "delete", "update"] + {{- end }} + {{- end }} + - apiGroups: ["snapshot.storage.k8s.io"] + resources: ["volumesnapshotclasses"] + verbs: ["get", "list", "watch"] + - apiGroups: ["snapshot.storage.k8s.io"] + resources: ["volumesnapshotcontents"] + verbs: ["create", "get", "list", "watch", "update", "delete", "patch"] + - apiGroups: ["snapshot.storage.k8s.io"] + resources: ["volumesnapshotcontents/status"] + verbs: ["update", "patch"] + - apiGroups: ["snapshot.storage.k8s.io"] + resources: ["volumesnapshots", "volumesnapshots/status"] + {{- if hasKey .Values.controller "vgsnapshot" }} + {{- if eq .Values.controller.vgsnapshot.enabled true }} + verbs: ["get", "list", "watch", "update", "create", "delete"] + {{- else }} + verbs: ["get", "list", "watch", "update"] + {{- end }} + {{- end }} + - apiGroups: ["storage.k8s.io"] + resources: ["volumeattachments/status"] + verbs: ["patch"] + - apiGroups: [""] + resources: ["pods"] + {{- if hasKey .Values "podmon" }} + {{- if eq .Values.podmon.enabled true }} + verbs: ["get", "list", "watch", "update", "delete"] + {{- else }} + verbs: ["get", "list", "watch"] + {{- end }} + {{- end }} + - apiGroups: ["apiextensions.k8s.io"] + resources: ["customresourcedefinitions"] + verbs: ["create", "list", "watch", "delete"] + - apiGroups: ["storage.k8s.io"] + resources: ["csinodes"] + verbs: ["get", "list", "watch"] + # below for resizer + - apiGroups: [""] + resources: ["persistentvolumeclaims/status"] + verbs: ["update", "patch"] + # below for dell-csi-replicator + {{- if hasKey .Values.controller "replication" }} + {{- if eq .Values.controller.replication.enabled true}} + - apiGroups: ["replication.storage.dell.com"] + resources: ["dellcsireplicationgroups"] + verbs: ["create", "delete", "get", "list", "patch", "update", "watch"] + - apiGroups: ["replication.storage.dell.com"] + resources: ["dellcsireplicationgroups/status"] + verbs: ["get", "patch", "update"] + - apiGroups: [""] + resources: ["configmaps"] + verbs: ["create", "delete", "get", "list", "watch", "update", "patch"] + {{- end}} + {{- end}} + # Permissions for CSIStorageCapacity + {{- if eq (include "csi-powerstore.isStorageCapacitySupported" .) "true" }} + - apiGroups: ["storage.k8s.io"] + resources: ["csistoragecapacities"] + verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] + - apiGroups: [""] + resources: ["pods"] + verbs: ["get"] + - apiGroups: ["apps"] + resources: ["replicasets"] + verbs: ["get"] + {{- end }} +--- + +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ .Release.Name }}-controller +subjects: + - kind: ServiceAccount + name: {{ .Release.Name }}-controller + namespace: {{ .Release.Namespace }} +roleRef: + kind: ClusterRole + name: {{ .Release.Name }}-controller + apiGroup: rbac.authorization.k8s.io + +--- + +kind: Deployment +apiVersion: apps/v1 +metadata: + name: {{ .Release.Name }}-controller + namespace: {{ .Release.Namespace }} +spec: + selector: + matchLabels: + name: {{ .Release.Name }}-controller + {{- if lt (.Values.controller.controllerCount | toString | atoi ) 1 -}} + {{- fail "value for .Values.controller.controllerCount should be atleast 1" }} + {{- else }} + replicas: {{ required "Must provide the number of controller instances to create." .Values.controller.controllerCount }} + {{- end }} + template: + metadata: + labels: + name: {{ .Release.Name }}-controller + annotations: + kubectl.kubernetes.io/default-container: driver + spec: + {{ if .Values.controller.nodeSelector }} + nodeSelector: + {{- toYaml .Values.controller.nodeSelector | nindent 8 }} + {{ end }} + {{ if .Values.controller.tolerations }} + tolerations: + {{- toYaml .Values.controller.tolerations | nindent 6 }} + {{ end }} + serviceAccountName: {{ .Release.Name }}-controller + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: "name" + operator: In + values: + - {{ .Release.Name }}-controller + topologyKey: "kubernetes.io/hostname" + containers: + {{- if hasKey .Values "podmon" }} + {{- if eq .Values.podmon.enabled true }} + - name: podmon + image: {{ required "Must provide the podmon container image." .Values.images.podmon.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + {{- toYaml .Values.podmon.controller.args | nindent 12 }} + env: + - name: MY_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: MY_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: MY_POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + - name: powerstore-config-params + mountPath: /powerstore-config-params + {{- end }} + {{- end }} + {{- if hasKey .Values "dev" }} + {{ if .Values.dev.enableTracing }}{{- include "pstore.tracing" . | nindent 8 }}{{ end }} + {{- end }} + - name: attacher + image: {{ required "Must provide the CSI attacher container image." .Values.images.attacher.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + - "--csi-address=$(ADDRESS)" + - "--v=5" + - "--leader-election" + - "--worker-threads=130" + - "--resync=10s" + - "--timeout=130s" + env: + - name: ADDRESS + value: /var/run/csi/csi.sock + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + {{- if hasKey .Values.controller "resizer" }} + {{- if eq .Values.controller.resizer.enabled true }} + - name: resizer + image: {{ required "Must provide the CSI resizer container image." .Values.images.resizer.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + - "--csi-address=$(ADDRESS)" + - "--v=5" + - "--leader-election" + env: + - name: ADDRESS + value: /var/run/csi/csi.sock + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + {{end}} + {{end}} + - name: provisioner + image: {{ required "Must provide the CSI provisioner container image." .Values.images.provisioner.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + - "--csi-address=$(ADDRESS)" + - "--volume-name-prefix={{ required "Must provide a value to prefix to driver created volume names" .Values.controller.volumeNamePrefix }}" + - "--volume-name-uuid-length=10" + - "--v=5" + - "--leader-election" + - "--default-fstype={{ .Values.defaultFsType | default "ext4" }}" + - "--extra-create-metadata" + - "--feature-gates=Topology=true" + - "--enable-capacity={{ (include "csi-powerstore.isStorageCapacitySupported" .) | default false }}" + - "--capacity-ownerref-level=2" + - "--capacity-poll-interval={{ .Values.storageCapacity.pollInterval | default "5m" }}" + env: + - name: ADDRESS + value: /var/run/csi/csi.sock + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + {{- if hasKey .Values.controller "snapshot" }} + {{- if eq .Values.controller.snapshot.enabled true }} + - name: snapshotter + image: {{ required "Must provide the CSI snapshotter container image." .Values.images.snapshotter.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + - "--csi-address=$(ADDRESS)" + - "--v=5" + - "--leader-election" + - "--snapshot-name-prefix={{ required "Must privided a Snapshot Name Prefix" .Values.controller.snapshot.snapNamePrefix }}" + env: + - name: ADDRESS + value: /var/run/csi/csi.sock + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + {{end}} + {{end}} + {{- if hasKey .Values.controller "vgsnapshot" }} + {{- if eq .Values.controller.vgsnapshot.enabled true }} + - name: vg-snapshotter + image: {{ required "Must provide the vgsnapshotter container image." .Values.images.vgsnapshotter.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + env: + - name: ADDRESS + value: /var/run/csi/csi.sock + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + {{- end }} + {{- end }} + {{- if hasKey .Values.controller "replication" }} + {{- if eq .Values.controller.replication.enabled true}} + - name: dell-csi-replicator + image: {{ required "Must provide the Dell CSI Replicator image." .Values.images.replication.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + - "--csi-address=$(ADDRESS)" + - "--leader-election=true" + - "--worker-threads=2" + - "--retry-interval-start=1s" + - "--retry-interval-max=300s" + - "--timeout=300s" + - "--context-prefix={{ .Values.controller.replication.replicationContextPrefix}}" + - "--prefix={{ .Values.controller.replication.replicationPrefix}}" + env: + - name: ADDRESS + value: /var/run/csi/csi.sock + - name: X_CSI_REPLICATION_CONFIG_DIR + value: /powerstore-config-params + - name: X_CSI_REPLICATION_CONFIG_FILE_NAME + value: driver-config-params.yaml + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + - name: powerstore-config-params + mountPath: /powerstore-config-params + {{- end }} + {{- end }} + {{- if hasKey .Values.controller "healthMonitor" }} + {{- if eq .Values.controller.healthMonitor.enabled true}} + - name: csi-external-health-monitor-controller + image: {{ required "Must provide the CSI external health monitor controller image." .Values.images.healthmonitor.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + - "--v=5" + - "--csi-address=$(ADDRESS)" + - "--leader-election" + - "--http-endpoint=:8080" + - "--enable-node-watcher=true" + - "--monitor-interval={{ .Values.controller.healthMonitor.interval | default "60s" }}" + - "--timeout=180s" + env: + - name: ADDRESS + value: /var/run/csi/csi.sock + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + {{- end }} + {{- end }} + - name: csi-metadata-retriever + image: {{ required "Must provide the CSI Metadata retriever container image." .Values.images.metadataretriever.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + command: [ "/csi-metadata-retriever" ] + env: + {{- if hasKey .Values "dev" }} + - name: ENABLE_TRACING + value: {{ .Values.dev.enableTracing | quote }} + {{ if .Values.dev.enableTracing }}{{- include "pstore.tracingenvvars" . | nindent 12 }}{{ end }} + {{- end }} + - name: CSI_RETRIEVER_ENDPOINT + value: /var/run/csi/csi_retriever.sock + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + - name: driver + image: {{ required "Must provide the PowerStore driver image repository." .Values.images.driver.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + command: [ "/csi-powerstore" ] + env: + {{- if hasKey .Values "dev" }} + - name: ENABLE_TRACING + value: {{ .Values.dev.enableTracing | quote }} + {{ if .Values.dev.enableTracing }}{{- include "pstore.tracingenvvars" . | nindent 12 }}{{ end }} + {{- end }} + - name: CSI_ENDPOINT + value: /var/run/csi/csi.sock + - name: CSI_RETRIEVER_ENDPOINT + value: /var/run/csi/csi_retriever.sock + - name: X_CSI_MODE + value: controller + - name: X_CSI_DRIVER_NAME + value: {{ .Values.driverName }} + - name: X_CSI_POWERSTORE_EXTERNAL_ACCESS + value: {{ .Values.externalAccess }} + - name: X_CSI_NFS_ACLS + value: "{{ .Values.nfsAcls }}" + - name: X_CSI_POWERSTORE_CONFIG_PATH + value: /powerstore-config/config + - name: X_CSI_POWERSTORE_CONFIG_PARAMS_PATH + value: /powerstore-config-params/driver-config-params.yaml + {{- if hasKey .Values "podmon" }} + - name: X_CSI_PODMON_ENABLED + value: "{{ .Values.podmon.enabled }}" + {{- if eq .Values.podmon.enabled true }} + {{- range $key, $value := .Values.podmon.controller.args }} + {{- if contains "--arrayConnectivityPollRate" $value }} + - name: X_CSI_PODMON_ARRAY_CONNECTIVITY_POLL_RATE + value: "{{ (split "=" $value)._1 }}" + {{- end }} + {{- end }} + {{- end }} + {{- end }} + - name: X_CSI_PODMON_API_PORT + value: "{{ .Values.podmonAPIPort }}" + {{- if hasKey .Values.controller "replication" }} + {{- if eq .Values.controller.replication.enabled true}} + - name: X_CSI_REPLICATION_CONTEXT_PREFIX + value: {{ .Values.controller.replication.replicationContextPrefix | default "powerstore"}} + - name: X_CSI_REPLICATION_PREFIX + value: {{ .Values.controller.replication.replicationPrefix | default "replication.storage.dell.com"}} + {{- end }} + {{- end }} + {{- if hasKey .Values.controller "healthMonitor" }} + {{- if eq .Values.controller.healthMonitor.enabled true}} + - name: X_CSI_HEALTH_MONITOR_ENABLED + value: "{{ .Values.controller.healthMonitor.enabled }}" + {{- end }} + {{- end }} + - name: GOPOWERSTORE_DEBUG + value: "true" + - name: CSI_AUTO_ROUND_OFF_FILESYSTEM_SIZE + value: "{{ .Values.allowAutoRoundOffFilesystemSize | default true }}" + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + - name: powerstore-config + mountPath: /powerstore-config + - name: powerstore-config-params + mountPath: /powerstore-config-params + volumes: + - name: socket-dir + emptyDir: + - name: powerstore-config-params + configMap: + name: {{ .Release.Name }}-config-params + - name: powerstore-config + secret: + secretName: {{ .Release.Name }}-config diff --git a/charts/dell/csi-powerstore/2.13.0/templates/csidriver.yaml b/charts/dell/csi-powerstore/2.13.0/templates/csidriver.yaml new file mode 100644 index 0000000000..3b40c879f1 --- /dev/null +++ b/charts/dell/csi-powerstore/2.13.0/templates/csidriver.yaml @@ -0,0 +1,29 @@ +# +# +# Copyright © 2020-2023 Dell Inc. or its subsidiaries. All Rights Reserved. +# +# 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: {{ .Values.driverName }} + labels: + security.openshift.io/csi-ephemeral-volume-profile: restricted +spec: + storageCapacity: {{ (include "csi-powerstore.isStorageCapacitySupported" .) | default false }} + podInfoOnMount: true + fsGroupPolicy: {{ .Values.fsGroupPolicy }} + volumeLifecycleModes: + - Persistent + - Ephemeral diff --git a/charts/dell/csi-powerstore/2.13.0/templates/driver-config-params.yaml b/charts/dell/csi-powerstore/2.13.0/templates/driver-config-params.yaml new file mode 100644 index 0000000000..ce5349de29 --- /dev/null +++ b/charts/dell/csi-powerstore/2.13.0/templates/driver-config-params.yaml @@ -0,0 +1,31 @@ +# +# +# Copyright © 2021-2023 Dell Inc. or its subsidiaries. All Rights Reserved. +# +# 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: ConfigMap +metadata: + name: {{ .Release.Name }}-config-params + namespace: {{ .Release.Namespace }} +data: + driver-config-params.yaml: | + CSI_LOG_LEVEL: "{{ .Values.logLevel }}" + CSI_LOG_FORMAT: "{{ .Values.logFormat }}" + {{ if .Values.podmon.enabled }} + PODMON_CONTROLLER_LOG_LEVEL: "{{ .Values.logLevel }}" + PODMON_CONTROLLER_LOG_FORMAT: "{{ .Values.logFormat }}" + PODMON_NODE_LOG_LEVEL: "{{ .Values.logLevel }}" + PODMON_NODE_LOG_FORMAT: "{{ .Values.logFormat }}" + {{ end }} \ No newline at end of file diff --git a/charts/dell/csi-powerstore/2.13.0/templates/node.yaml b/charts/dell/csi-powerstore/2.13.0/templates/node.yaml new file mode 100644 index 0000000000..7696198d48 --- /dev/null +++ b/charts/dell/csi-powerstore/2.13.0/templates/node.yaml @@ -0,0 +1,353 @@ +# +# +# Copyright © 2020-2023 Dell Inc. or its subsidiaries. All Rights Reserved. +# +# 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: {{ .Release.Name }}-node + namespace: {{ .Release.Namespace }} + +--- + +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ .Release.Name }}-node +rules: + - apiGroups: [""] + resources: ["persistentvolumes"] + verbs: ["create", "delete", "get", "list", "watch", "update"] + - apiGroups: [""] + resources: ["persistentvolumesclaims"] + verbs: ["get", "list", "watch", "update"] + - apiGroups: [""] + resources: ["events"] + verbs: ["get", "list", "watch", "create", "update", "patch"] + - apiGroups: [""] + resources: ["nodes"] + verbs: ["get", "list", "watch", "update", "patch"] + - apiGroups: ["storage.k8s.io"] + resources: ["volumeattachments"] + verbs: ["get", "list", "watch", "update"] + - apiGroups: ["storage.k8s.io"] + resources: ["storageclasses"] + verbs: ["get", "list", "watch"] + - apiGroups: ["storage.k8s.io"] + resources: ["volumeattachments"] + verbs: ["get", "list", "watch", "update"] + - apiGroups: ["security.openshift.io"] + resourceNames: ["privileged"] + resources: ["securitycontextconstraints"] + verbs: ["use"] + {{- if hasKey .Values "podmon" }} + {{- if eq .Values.podmon.enabled true }} + - apiGroups: [""] + resources: ["pods"] + verbs: ["get", "list", "watch", "update", "delete"] + - apiGroups: ["coordination.k8s.io"] + resources: ["leases"] + verbs: ["get", "watch", "list", "delete", "update", "create"] + {{ end }} + {{ end }} + +--- + +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ .Release.Name }}-node +subjects: + - kind: ServiceAccount + name: {{ .Release.Name }}-node + namespace: {{ .Release.Namespace }} +roleRef: + kind: ClusterRole + name: {{ .Release.Name }}-node + apiGroup: rbac.authorization.k8s.io + +--- + +kind: DaemonSet +apiVersion: apps/v1 +metadata: + name: {{ .Release.Name }}-node + namespace: {{ .Release.Namespace }} +spec: + selector: + matchLabels: + app: {{ .Release.Name }}-node + template: + metadata: + labels: + app: {{ .Release.Name }}-node + {{- if .Values.podmon.enabled }} + driver.dellemc.com: dell-storage + {{- end }} + annotations: + kubectl.kubernetes.io/default-container: driver + spec: + {{ if .Values.node.nodeSelector }} + nodeSelector: + {{- toYaml .Values.node.nodeSelector | nindent 8 }} + {{ end }} + {{ if .Values.node.tolerations }} + tolerations: + {{- toYaml .Values.node.tolerations | nindent 6 }} + {{ end }} + serviceAccount: {{ .Release.Name }}-node + dnsPolicy: ClusterFirstWithHostNet + hostNetwork: true + hostIPC: true + containers: + {{- if hasKey .Values "podmon" }} + {{- if eq .Values.podmon.enabled true }} + - name: podmon + securityContext: + privileged: true + capabilities: + add: ["SYS_ADMIN"] + allowPrivilegeEscalation: true + image: {{ required "Must provide the podmon container image." .Values.images.podmon.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + {{- toYaml .Values.podmon.node.args | nindent 12 }} + env: + - name: KUBE_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + - name: X_CSI_PRIVATE_MOUNT_DIR + value: {{ .Values.kubeletConfigDir }} + - name: MY_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: MY_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: MY_POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + volumeMounts: + - name: kubelet-pods + mountPath: {{ .Values.kubeletConfigDir }}/pods + mountPropagation: "Bidirectional" + - name: driver-path + mountPath: {{ .Values.kubeletConfigDir }}/plugins/{{ .Values.driverName }} + mountPropagation: "Bidirectional" + - name: csi-path + mountPath: {{ .Values.kubeletConfigDir }}/plugins/kubernetes.io/csi + mountPropagation: "Bidirectional" + - name: dev + mountPath: /dev + - name: usr-bin + mountPath: /usr-bin + - name: var-run + mountPath: /var/run + - name: powerstore-config-params + mountPath: /powerstore-config-params + {{- end }} + {{- end }} + {{- if hasKey .Values "dev" }} + {{ if .Values.dev.enableTracing }}{{- include "pstore.tracing" . | nindent 8 }}{{ end }} + {{- end}} + - name: driver + securityContext: + privileged: true + capabilities: + add: ["SYS_ADMIN"] + allowPrivilegeEscalation: true + image: {{ required "Must provide the Powerstore driver image repository." .Values.images.driver.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + command: [ "/csi-powerstore" ] + env: + {{- if hasKey .Values "dev" }} + - name: ENABLE_TRACING + value: {{ .Values.dev.enableTracing | quote}} + {{ if .Values.dev.enableTracing }}{{- include "pstore.tracingenvvars" . | nindent 12 }}{{ end }} + {{- end}} + - name: CSI_ENDPOINT + value: unix://{{ .Values.kubeletConfigDir }}/plugins/{{ .Values.driverName }}/csi_sock + - name: X_CSI_MODE + value: node + - name: X_CSI_POWERSTORE_KUBE_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + - name: X_CSI_POWERSTORE_NODE_NAME_PREFIX + value: {{ .Values.node.nodeNamePrefix }} + - name: X_CSI_POWERSTORE_NODE_ID_PATH + value: /node-id + - name: X_CSI_POWERSTORE_MAX_VOLUMES_PER_NODE + value: "{{ .Values.maxPowerstoreVolumesPerNode }}" + - name: X_CSI_POWERSTORE_NODE_CHROOT_PATH + value: /noderoot + - name: X_CSI_POWERSTORE_TMP_DIR + value: {{ .Values.kubeletConfigDir }}/plugins/{{ .Values.driverName }}/tmp + - name: X_CSI_DRIVER_NAME + value: {{ .Values.driverName }} + - name: X_CSI_FC_PORTS_FILTER_FILE_PATH + value: {{ .Values.nodeFCPortsFilterFile }} + {{- if eq .Values.connection.enableCHAP true }} + - name: X_CSI_POWERSTORE_ENABLE_CHAP + value: "true" + {{- else }} + - name: X_CSI_POWERSTORE_ENABLE_CHAP + value: "false" + {{- end }} + - name: X_CSI_POWERSTORE_CONFIG_PATH + value: /powerstore-config/config + - name: X_CSI_POWERSTORE_CONFIG_PARAMS_PATH + value: /powerstore-config-params/driver-config-params.yaml + - name: GOPOWERSTORE_DEBUG + value: "true" + {{- if hasKey .Values.node "healthMonitor" }} + {{- if eq .Values.node.healthMonitor.enabled true}} + - name: X_CSI_HEALTH_MONITOR_ENABLED + value: "{{ .Values.controller.healthMonitor.enabled }}" + {{- end }} + {{- end }} + {{- if hasKey .Values "podmon" }} + - name: X_CSI_PODMON_ENABLED + value: "{{ .Values.podmon.enabled }}" + {{- if eq .Values.podmon.enabled true }} + {{- range $key, $value := .Values.podmon.node.args }} + {{- if contains "--arrayConnectivityPollRate" $value }} + - name: X_CSI_PODMON_ARRAY_CONNECTIVITY_POLL_RATE + value: "{{ (split "=" $value)._1 }}" + {{- end }} + {{- end }} + {{- end }} + {{- end }} + - name: X_CSI_PODMON_API_PORT + value: "{{ .Values.podmonAPIPort }}" + volumeMounts: + - name: driver-path + mountPath: {{ .Values.kubeletConfigDir }}/plugins/{{ .Values.driverName }} + - name: csi-path + mountPath: {{ .Values.kubeletConfigDir }}/plugins/kubernetes.io/csi + mountPropagation: "Bidirectional" + - name: pods-path + mountPath: {{ .Values.kubeletConfigDir }}/pods + mountPropagation: "Bidirectional" + - name: dev + mountPath: /dev + - name: sys + mountPath: /sys + - name: run + mountPath: /run + - name: node-id + mountPath: /node-id + - name: etciscsi + mountPath: /etc/iscsi + - name: mpath + mountPath: /etc/multipath.conf + - name: noderoot + mountPath: /noderoot + - name: powerstore-config + mountPath: /powerstore-config + - name: powerstore-config-params + mountPath: /powerstore-config-params + - name: registrar + image: {{ required "Must provide the CSI node registrar container image." .Values.images.registrar.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + - "--v=5" + - "--csi-address=$(ADDRESS)" + - --kubelet-registration-path={{ .Values.kubeletConfigDir }}/plugins/{{ .Values.driverName }}/csi_sock + env: + - name: ADDRESS + value: /csi/csi_sock + - name: KUBE_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + volumeMounts: + - name: registration-dir + mountPath: /registration + - name: driver-path + mountPath: /csi + volumes: + - name: registration-dir + hostPath: + path: {{ .Values.kubeletConfigDir }}/plugins_registry/ + type: DirectoryOrCreate + - name: driver-path + hostPath: + path: {{ .Values.kubeletConfigDir }}/plugins/{{ .Values.driverName }} + type: DirectoryOrCreate + - name: csi-path + hostPath: + path: {{ .Values.kubeletConfigDir }}/plugins/kubernetes.io/csi + - name: pods-path + hostPath: + path: {{ .Values.kubeletConfigDir }}/pods + type: Directory + - name: dev + hostPath: + path: /dev + type: Directory + - name: node-id + hostPath: + path: {{ required "Must provide the path to file with node identifier." .Values.node.nodeIDPath }} + type: File + - name: etciscsi + hostPath: + path: /etc/iscsi + type: DirectoryOrCreate + - name: mpath + hostPath: + path: /etc/multipath.conf + type: FileOrCreate + - name: noderoot + hostPath: + path: / + type: Directory + - name: sys + hostPath: + path: /sys + type: Directory + - name: run + hostPath: + path: /run + type: Directory + - name: powerstore-config-params + configMap: + name: {{ .Release.Name }}-config-params + - name: powerstore-config + secret: + secretName: {{ .Release.Name }}-config + {{- if hasKey .Values "podmon" }} + {{- if eq .Values.podmon.enabled true }} + - name: usr-bin + hostPath: + path: /usr/bin + type: Directory + - name: kubelet-pods + hostPath: + path: /var/lib/kubelet/pods + type: Directory + - name: var-run + hostPath: + path: /var/run + type: Directory + {{ end }} + {{ end }} diff --git a/charts/dell/csi-powerstore/2.13.0/values.yaml b/charts/dell/csi-powerstore/2.13.0/values.yaml new file mode 100644 index 0000000000..68a2ba407b --- /dev/null +++ b/charts/dell/csi-powerstore/2.13.0/values.yaml @@ -0,0 +1,363 @@ +# +# +# Copyright © 2020-2023 Dell Inc. or its subsidiaries. All Rights Reserved. +# +# 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. +# +# + +## K8S/DRIVER ATTRIBUTES +######################## + +# driverName: defines the name of driver +# Allowed values: string +# Default value: None +driverName: "csi-powerstore.dellemc.com" +# "version" is used to verify the values file matches driver version +# Not recommend to change +version: v2.13.0 + +# "images" defines every container images used for the driver and its sidecars. +# To use your own images, or a private registry, change the values here. +images: + # "driver" defines the container image, used for the driver container. + driver: + image: quay.io/dell/container-storage-modules/csi-powerstore:v2.13.0 + # CSI sidecars + attacher: + image: registry.k8s.io/sig-storage/csi-attacher:v4.8.0 + provisioner: + image: registry.k8s.io/sig-storage/csi-provisioner:v5.1.0 + snapshotter: + image: registry.k8s.io/sig-storage/csi-snapshotter:v8.2.0 + resizer: + image: registry.k8s.io/sig-storage/csi-resizer:v1.13.1 + registrar: + image: registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.13.0 + healthmonitor: + image: registry.k8s.io/sig-storage/csi-external-health-monitor-controller:v0.14.0 + + # CSM sidecars + replication: + image: quay.io/dell/container-storage-modules/dell-csi-replicator:v1.11.0 + vgsnapshotter: + image: quay.io/dell/container-storage-modules/csi-volumegroup-snapshotter:v1.8.1 + podmon: + image: quay.io/dell/container-storage-modules/podmon:v1.12.0 + metadataretriever: + image: quay.io/dell/container-storage-modules/csi-metadata-retriever:v1.10.0 + +# Specify kubelet config dir path. +# Ensure that the config.yaml file is present at this path. +# Default value: /var/lib/kubelet +kubeletConfigDir: /var/lib/kubelet + +# nodeFCPortsFilterFile: It is the name of the environment variable which store path to the file which +# provide list of WWPN which should be used by the driver for FC connection on this node +# If file not exist or empty or in invalid format, then the driver will use all available FC ports +# Allowed Values: string +# Default Value: None +# Example: +# content of the file: +# 21:00:00:29:ff:48:9f:6e,21:00:00:29:ff:48:9f:6e +nodeFCPortsFilterFile: /etc/fc-ports-filter + +# externalAccess: allows to specify additional entries for hostAccess of NFS volumes. Both single IP address and subnet are valid entries. +# Allowed Values: x.x.x.x/xx or x.x.x.x +# Default Value: None +externalAccess: + +# imagePullPolicy: Policy to determine if the image should be pulled prior to starting the container. +# Allowed values: +# Always: Always pull the image. +# IfNotPresent: Only pull the image if it does not already exist on the node. +# Never: Never pull the image. +# Default value: None +imagePullPolicy: IfNotPresent + +# maxPowerstoreVolumesPerNode: Specify default value for maximum number of volumes that controller can publish to the node. +# If value is zero CO SHALL decide how many volumes of this type can be published by the controller to the node. +# This limit is applicable to all the nodes in the cluster for which node label 'max-powerstore-volumes-per-node' is not set. +# Allowed values: n, where n >= 0 +# Default value: 0 +maxPowerstoreVolumesPerNode: 0 + +# nfsAcls: enables setting permissions on NFS mount directory +# This value acts as default value for NFS ACL (nfsAcls), if not specified for an array config in secret +# Permissions can be specified in two formats: +# 1) Unix mode (NFSv3) +# 2) NFSv4 ACLs (NFSv4) +# NFSv4 ACLs are supported on NFSv4 share only. +# Allowed values: +# 1) Unix mode: valid octal mode number +# Examples: "0777", "777", "0755" +# 2) NFSv4 acls: valid NFSv4 acls, seperated by comma +# Examples: "A::OWNER@:RWX,A::GROUP@:RWX", "A::OWNER@:rxtncy" +# Optional: true +# Default value: "0777" +nfsAcls: "0777" + +# podmonAPIPort: Defines the port to be used within the kubernetes cluster +# Allowed values: +# Any valid and free port. +# Default value: 8083 +podmonAPIPort: 8083 + +# controller: configure controller specific parameters +controller: + # controllerCount: defines the number of csi-powerstore controller pods to deploy to + # the Kubernetes release. + # Allowed values: n, where n > 0 + # Default value: None + controllerCount: 2 + + # volumeNamePrefix: defines a string prepended to each volume created by the CSI driver. + # Allowed values: string + # Default value: csivol + # Examples: "k8s", "app1" + volumeNamePrefix: csivol + + # vgsnapshot: allows to configure volume-group-snapshot + # volume-group-snapshot CRDs must be installed before installing driver + vgsnapshot: + # enabled: Enable/Disable volume-group-snapshot feature + # Allowed values: + # true: enable volume-group-snapshot feature(install vg-snapshotter sidecar) + # false: disable volume-group-snapshot feature(do not install vg-snapshotter sidecar) + # Default value: false + enabled: false + + # snapshot: allows to enable/disable snapshot feature + # snapshot CRDs needs to be installed before enabling this feature + snapshot: + # enabled: Enable/Disable volume snapshot feature + # Allowed values: + # true: enable volume snapshot feature(install snapshotter sidecar) + # false: disable volume snapshot feature(do not install snapshotter sidecar) + # Default value: None + enabled: true + + # snapNamePrefix: Prefix to apply to the names of a created snapshots + # Allowed values: string + # Default value: csi-snap + # Examples: "snap", "snapshot" + snapNamePrefix: csi-snap + # resizer: allows to enable/disable resizer feature + resizer: + # enabled: Enable/Disable volume expansion feature + # Allowed values: + # true: enable volume expansion feature(install resizer sidecar) + # false: disable volume expansion feature(do not install resizer sidecar) + # Default value: true + enabled: true + + healthMonitor: + # enabled: Enable/Disable health monitor of CSI volumes + # Allowed values: + # true: enable checking of health condition of CSI volumes + # false: disable checking of health condition of CSI volumes + # Default value: false + enabled: false + + # interval: Interval of monitoring volume health condition + # Allowed values: Number followed by unit (s,m,h) + # Examples: 60s, 5m, 1h + # Default value: 60s + interval: 60s + + # replication: allows to configure replication + # Replication CRDs must be installed before installing driver + replication: + # enabled: Enable/Disable replication feature + # Allowed values: + # true: enable replication feature(install dell-csi-replicator sidecar) + # false: disable replication feature(do not install dell-csi-replicator sidecar) + # Default value: false + enabled: false + + # replicationContextPrefix: prefix to use for naming of resources created by replication feature + # Allowed values: string + # Default value: powerstore + replicationContextPrefix: "powerstore" + + # replicationPrefix: prefix to prepend to storage classes parameters + # Allowed values: string + # Default value: replication.storage.dell.com + replicationPrefix: "replication.storage.dell.com" + + # nodeSelector: Define node selection constraints for controller pods. + # For the pod to be eligible to run on a node, the node must have each + # of the indicated key-value pairs as labels. + # Leave as blank to consider all nodes + # Allowed values: map of key-value pairs + # Default value: None + nodeSelector: + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/master taint + # node-role.kubernetes.io/master + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/control-plane taint + # node-role.kubernetes.io/control-plane + + # tolerations: Define tolerations for the controllers, if required. + # Leave as blank to install controller on worker nodes + # Default value: None + tolerations: + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/master taint + # - key: "node-role.kubernetes.io/master" + # operator: "Exists" + # effect: "NoSchedule" + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/control-plane taint + # tolerations: + # - key: "node-role.kubernetes.io/control-plane" + # operator: "Exists" + # effect: "NoSchedule" + +# node: configure node pod specific parameters +node: + # nodeNamePrefix: defines a string prepended to each node registered by the CSI driver. + # Allowed values: string + # Default value: None + nodeNamePrefix: csi-node + + # nodeIDPath: defines the path to file with node identifier (e.g. /etc/machine-id, /etc/hostname). + # Allowed values: string + # Default value: None + nodeIDPath: /etc/machine-id + + healthMonitor: + # enabled: Enable/Disable health monitor of CSI volumes- volume usage, volume condition + # Allowed values: + # true: enable checking of health condition of CSI volumes + # false: disable checking of health condition of CSI volumes + # Default value: None + enabled: false + + # nodeSelector: Define node selection constraints for node pods. + # For the pod to be eligible to run on a node, the node must have each + # of the indicated key-value pairs as labels. + # Leave as blank to consider all nodes + # Allowed values: map of key-value pairs + # Default value: None + nodeSelector: + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/master taint + # node-role.kubernetes.io/master + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/control-plane taint + # node-role.kubernetes.io/control-plane + + # tolerations: Define tolerations for the node pods, if required. + # Leave as blank to consider all worker nodes + # Default value: None + tolerations: + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/master taint + # - key: "node-role.kubernetes.io/master" + # operator: "Exists" + # effect: "NoSchedule" + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/control-plane taint + # tolerations: + # - key: "node-role.kubernetes.io/control-plane" + # operator: "Exists" + # effect: "NoSchedule" + + # Uncomment if CSM for Resiliency and CSI Driver pods monitor are enabled + # tolerations: + # - key: "offline.vxflexos.storage.dell.com" + # operator: "Exists" + # effect: "NoSchedule" + # - key: "vxflexos.podmon.storage.dell.com" + # operator: "Exists" + # effect: "NoSchedule" + # - key: "offline.unity.storage.dell.com" + # operator: "Exists" + # effect: "NoSchedule" + # - key: "unity.podmon.storage.dell.com" + # operator: "Exists" + # effect: "NoSchedule" + # - key: "offline.isilon.storage.dell.com" + # operator: "Exists" + # effect: "NoSchedule" + # - key: "isilon.podmon.storage.dell.com" + # operator: "Exists" + # effect: "NoSchedule" + # - key: "offline.powerstore.storage.dell.com" + # operator: "Exists" + # effect: "NoSchedule" + # - key: "powerstore.podmon.storage.dell.com" + # operator: "Exists" + # effect: "NoSchedule" + +## PLATFORM ATTRIBUTES +###################### + +# connection: allows to configure connection to storage array +connection: + # connection.enableCHAP: allows to enable CHAP for iSCSI connections + # CHAP password will be autogenerated by driver + # Allowed values: + # true : enable CHAP + # false: disable CHAP + # Default value: false + enableCHAP: false + +# CSI driver log level +# Allowed values: "error", "warn"/"warning", "info", "debug", "error" +# Default value: "info" +logLevel: "info" + +# CSI driver log format +# Allowed values: "TEXT" or "JSON" +# Default value: "JSON" +logFormat: "JSON" + +# Following modes are supported: None, File and ReadWriteOnceWithFSType +fsGroupPolicy: ReadWriteOnceWithFSType + +# Allows the controller to round off filesystem to 3Gi which is the minimum supported value +allowAutoRoundOffFilesystemSize: true + +# Storage Capacity Tracking +# Note: Capacity tracking is supported in kubernetes v1.24 and above, this feature will be automatically disabled in older versions. +storageCapacity: + # enabled : Enable/Disable storage capacity tracking + # Allowed values: + # true: enable storage capacity tracking + # false: disable storage capacity tracking + # Default value: true + enabled: true + # pollInterval : Configure how often external-provisioner polls the driver to detect changed capacity + # Allowed values: 1m,2m,3m,...,10m,...,60m etc + # Default value: 5m + pollInterval: 5m + +# Enable this feature only after contact support for additional information +podmon: + enabled: false + controller: + args: + - "--csisock=unix:/var/run/csi/csi.sock" + - "--labelvalue=csi-powerstore" + - "--arrayConnectivityPollRate=60" + - "--driverPath=csi-powerstore.dellemc.com" + - "--mode=controller" + - "--skipArrayConnectionValidation=false" + - "--driver-config-params=/powerstore-config-params/driver-config-params.yaml" + - "--driverPodLabelValue=dell-storage" + - "--ignoreVolumelessPods=false" + + node: + args: + - "--csisock=unix:/var/lib/kubelet/plugins/csi-powerstore.dellemc.com/csi_sock" + - "--labelvalue=csi-powerstore" + - "--arrayConnectivityPollRate=60" + - "--driverPath=csi-powerstore.dellemc.com" + - "--mode=node" + - "--leaderelection=false" + - "--driver-config-params=/powerstore-config-params/driver-config-params.yaml" + - "--driverPodLabelValue=dell-storage" + - "--ignoreVolumelessPods=false" diff --git a/charts/dell/csi-unity/2.13.0/Chart.yaml b/charts/dell/csi-unity/2.13.0/Chart.yaml new file mode 100644 index 0000000000..cca6e291d3 --- /dev/null +++ b/charts/dell/csi-unity/2.13.0/Chart.yaml @@ -0,0 +1,22 @@ +annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Dell CSI Unity + catalog.cattle.io/kube-version: '>= 1.24.0' + catalog.cattle.io/release-name: unity +apiVersion: v2 +appVersion: 2.13.0 +description: 'Unity XT CSI (Container Storage Interface) driver Kubernetes integration. + This chart includes everything required to provision via CSI as well as a Unity + XT StorageClass. ' +icon: file://assets/icons/csi-unity.png +keywords: +- csi +- storage +kubeVersion: '>= 1.24.0' +maintainers: +- name: DellEMC +name: csi-unity +sources: +- https://github.com/dell/csi-unity +type: application +version: 2.13.0 diff --git a/charts/dell/csi-unity/2.13.0/app-readme.md b/charts/dell/csi-unity/2.13.0/app-readme.md new file mode 100644 index 0000000000..9c7fe7bd5d --- /dev/null +++ b/charts/dell/csi-unity/2.13.0/app-readme.md @@ -0,0 +1,93 @@ +The [CSI Driver for Unity XT](https://github.com/dell/csi-unity) is part of the CSM (Container Storage Modules) open-source suite of Kubernetes storage enablers for Dell products. CSI Driver for Unity XT is a Container Storage Interface (CSI) driver that provides support for provisioning persistent storage using Dell Unity XT storage array. + + +## Pre-Requisites +- Install Kubernetes (see [supported versions](https://dell.github.io/csm-docs/docs/csidriver/#features-and-capabilities)) +- Install Helm v3 (follow [steps](https://dell.github.io/csm-docs/docs/csidriver/installation/helm/unity/#install-helm-30)) +- Install sshpass +- Configure the pre-installation steps according to the protocols you are using: + - To use FC protocol, the host must be zoned with Unity XT array and Multipath needs to be configured + - To use iSCSI protocol, iSCSI initiator utils packages need to be installed and Multipath needs to be configured + - To use NFS protocol, NFS utility packages needs to be installed +- Enable mount propagation on container runtime that is being used +- In order to use the Kubernetes Volume Snapshot feature, ensure to deploy `Volume Snapshot CRDs` and `Volume Snapshot Controller` in the kubernetes cluster as a pre-requisite. Refer [here](https://dell.github.io/csm-docs/docs/csidriver/installation/helm/unity/#installation-example) for installation example of CRD's and default snapshot controller + +For more information, refer to the [documentation](https://dell.github.io/csm-docs/docs/csidriver/installation/helm/unity/#prerequisites) + +## Install CSI Driver for Unity XT + +1. Clone the [git repository](https://github.com/dell/csi-unity) that has the helm charts and install scripts +2. Create a namespace called `unity` +3. Collect information from the Unity XT Systems like Unique ArrayId, IP address, username, and password. Using the information, prepare `secrets.yaml`. Create the secrets. Samples available [here](https://github.com/dell/csi-unity/blob/main/samples/secret/secret.yaml) +>NOTE: For certificate validation of Unisphere REST API calls refer [here](https://dell.github.io/csm-docs/docs/csidriver/installation/helm/unity/#certificate-validation-for-unisphere-rest-api-calls). Otherwise, create an empty secret. Samples available [here](https://github.com/dell/csi-unity/tree/main/samples/secret/emptysecret.yaml) +4. Copy the `helm/csi-unity/values.yaml` into a file named `myvalues.yaml` in the same directory of csi-install.sh, to customize settings for installation +5. Edit `myvalues.yaml` to set the following parameters for your installation: + + The following table lists the primary configurable parameters of the Unity XT driver chart and their default values. More detailed information can be found in the [`values.yaml`](https://github.com/dell/csi-unity/blob/master/helm/csi-unity/values.yaml) file in this repository. + + | Parameter | Description | Required | Default | + | --------- | ----------- | -------- |-------- | + | version | helm version | true | - | + | logLevel | LogLevel is used to set the logging level of the driver | true | info | + | allowRWOMultiPodAccess | Flag to enable multiple pods to use the same PVC on the same node with RWO access mode. | false | false | + | kubeletConfigDir | Specify kubelet config dir path | Yes | /var/lib/kubelet | + | syncNodeInfoInterval | Time interval to add node info to the array. Default 15 minutes. The minimum value should be 1 minute. | false | 15 | + | maxUnityVolumesPerNode | Maximum number of volumes that controller can publish to the node. | false | 0 | + | certSecretCount | Represents the number of certificate secrets, which the user is going to create for SSL authentication. (unity-cert-0..unity-cert-n). The minimum value should be 1. | false | 1 | + | imagePullPolicy | The default pull policy is IfNotPresent which causes the Kubelet to skip pulling an image if it already exists. | Yes | IfNotPresent | + | podmon.enabled | service to monitor failing jobs and notify | false | - | + | podmon.image| pod man image name | false | - | + | tenantName | Tenant name added while adding host entry to the array | No | | + | fsGroupPolicy | Defines which FS Group policy mode to be used, Supported modes `None, File and ReadWriteOnceWithFSType` | No | "ReadWriteOnceWithFSType" | + | **controller** | Allows configuration of the controller-specific parameters.| - | - | + | controllerCount | Defines the number of csi-unity controller pods to deploy to the Kubernetes release| Yes | 2 | + | volumeNamePrefix | Defines a string prefix for the names of PersistentVolumes created | Yes | "k8s" | + | snapshot.enabled | Enable/Disable volume snapshot feature | Yes | true | + | snapshot.snapNamePrefix | Defines a string prefix for the names of the Snapshots created | Yes | "snapshot" | + | resizer.enabled | Enable/Disable volume expansion feature | Yes | true | + | nodeSelector | Define node selection constraints for pods of controller deployment | No | | + | tolerations | Define tolerations for the controller deployment, if required | No | | + | healthMonitor.enabled | Enable/Disable deployment of external health monitor sidecar for controller side volume health monitoring. | No | false | + | healthMonitor.interval | Interval of monitoring volume health condition. Allowed values: Number followed by unit (s,m,h) | No | 60s | + | ***node*** | Allows configuration of the node-specific parameters.| - | - | + | dnsPolicy | Define the DNS Policy of the Node service | Yes | ClusterFirstWithHostNet | + | healthMonitor.enabled | Enable/Disable health monitor of CSI volumes- volume usage, volume condition | No | false | + | nodeSelector | Define node selection constraints for pods of node deployment | No | | + | tolerations | Define tolerations for the node deployment, if required | No | | + + + **Note**: + + * User should provide all boolean values with double-quotes. This applies only for `myvalues.yaml`. Example: "true"/"false" + * controllerCount parameter value should be <= number of nodes in the kubernetes cluster else install script fails + +6. Run the `./csi-install.sh --namespace unity --values ./myvalues.yaml` command to proceed with the installation using bash script or you can also install the driver using standalone helm chart by running helm install command `helm install --dry-run --values --namespace `
+ `` - namespace of the driver installation
+ `` - unity in case of unity-creds and unity-certs-0 secrets
+ `` - Path of the helm directory
+ +7. Create storage classes from [samples](https://github.com/dell/csi-unity/tree/main/samples/storageclass) + + **Note**: + + * At least one storage class is required for one array + * In case you want to make updates to an existing storage class, ensure to delete it using the `kubectl delete storageclass ` command. Deleting a storage class has no impact on a running Pod with mounted PVCs. You cannot provision new PVCs until at least one storage class is newly created + +For full-length documentation, please visit Container Storage Modules documentation [page](https://dell.github.io/csm-docs/). + +## Support + +The CSI Driver for Dell Unity XT is fully supported by DELL. + +For all your support needs or to follow the latest ongoing discussions and updates, join our Slack group. Click [Here](http://del.ly/Slack_request) to request your invite. + +You can also interact with us on [GitHub](https://github.com/dell/csm) by creating a [GitHub Issue](https://github.com/dell/csm/issues). + +## Contributing + +We value all feedback and contributions. If you find any issues or want to contribute, please feel free to open an issue or file a PR. More details in [Contribution Guidelines](https://dell.github.io/csm-docs/docs/references/contributionguidelines/). + +## License + +This is open source software licensed using the Apache License 2.0. Please see [LICENSE](https://github.com/dell/csi-powerstore/blob/main/licenses/Apache.txt) for details. + diff --git a/charts/dell/csi-unity/2.13.0/templates/_helpers.tpl b/charts/dell/csi-unity/2.13.0/templates/_helpers.tpl new file mode 100644 index 0000000000..4031377c2a --- /dev/null +++ b/charts/dell/csi-unity/2.13.0/templates/_helpers.tpl @@ -0,0 +1,10 @@ +{{/* +Return true if storage capacity tracking is enabled and is supported based on k8s version +*/}} +{{- define "csi-unity.isStorageCapacitySupported" -}} +{{- if eq .Values.storageCapacity.enabled true -}} + {{- if and (eq .Capabilities.KubeVersion.Major "1") (ge (trimSuffix "+" .Capabilities.KubeVersion.Minor) "24") -}} + {{- true -}} + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/dell/csi-unity/2.13.0/templates/controller.yaml b/charts/dell/csi-unity/2.13.0/templates/controller.yaml new file mode 100644 index 0000000000..80ecbcac61 --- /dev/null +++ b/charts/dell/csi-unity/2.13.0/templates/controller.yaml @@ -0,0 +1,328 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Release.Name }}-controller + namespace: {{ .Release.Namespace }} +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ .Release.Name }}-controller +rules: + - apiGroups: ["coordination.k8s.io"] + resources: ["leases"] + verbs: ["get", "watch", "list", "delete", "update", "create"] + - apiGroups: [""] + resources: ["events"] + verbs: ["list", "watch", "create", "update", "patch"] + - apiGroups: [""] + resources: ["nodes"] +{{- if .Values.podmon.enabled }} + verbs: ["get", "list", "watch", "patch"] +{{- else }} + verbs: ["get", "list", "watch"] +{{- end }} + - apiGroups: [""] + resources: ["persistentvolumes"] + verbs: ["get", "list", "watch", "create", "delete", "update","patch"] + - apiGroups: [""] + resources: ["persistentvolumeclaims"] + verbs: ["get", "list", "create", "watch", "update"] + - apiGroups: ["storage.k8s.io"] + resources: ["storageclasses"] + verbs: ["get", "list", "watch"] + - apiGroups: ["storage.k8s.io"] + resources: ["volumeattachments"] +{{- if .Values.podmon.enabled }} + verbs: ["get", "list", "watch", "update", "patch", "delete"] +{{- else }} + verbs: ["get", "list", "watch", "update","patch"] +{{- end }} + - apiGroups: ["storage.k8s.io"] + resources: ["csinodes"] + verbs: ["get", "list", "watch", "update"] + - apiGroups: ["storage.k8s.io"] + resources: ["volumeattachments/status"] + verbs: ["patch"] + - apiGroups: ["csi.storage.k8s.io"] + resources: ["csinodeinfos"] + verbs: ["get", "list", "watch"] + - apiGroups: [""] + resources: ["pods"] +{{- if .Values.podmon.enabled }} + verbs: ["get", "list", "watch", "update", "delete"] +{{- else }} + verbs: ["get", "list", "watch"] +{{- end }} +# below for snapshotter + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "list"] + - apiGroups: ["snapshot.storage.k8s.io"] + resources: ["volumesnapshotclasses"] + verbs: ["get", "list", "watch"] + - apiGroups: ["snapshot.storage.k8s.io"] + resources: ["volumesnapshotcontents"] + verbs: ["create", "get", "list", "watch", "update", "delete", "patch"] + - apiGroups: ["snapshot.storage.k8s.io"] + resources: ["volumesnapshots/status"] + verbs: ["update"] + - apiGroups: ["storage.k8s.io"] + resources: ["volumeattachments/status"] + verbs: ["patch"] + - apiGroups: ["snapshot.storage.k8s.io"] + resources: ["volumesnapshots"] + verbs: ["get", "list", "watch", "update"] + - apiGroups: ["apiextensions.k8s.io"] + resources: ["customresourcedefinitions"] + verbs: ["create", "list", "watch", "delete"] + - apiGroups: ["snapshot.storage.k8s.io"] + resources: ["volumesnapshotcontents/status"] + verbs: ["update", "patch"] + # below for resizer + - apiGroups: [""] + resources: ["persistentvolumeclaims/status"] + verbs: ["update", "patch"] + # Permissions for CSIStorageCapacity + {{- if eq (include "csi-unity.isStorageCapacitySupported" .) "true" }} + - apiGroups: ["storage.k8s.io"] + resources: ["csistoragecapacities"] + verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] + - apiGroups: [""] + resources: ["pods"] + verbs: ["get"] + - apiGroups: ["apps"] + resources: ["replicasets"] + verbs: ["get"] + {{- end }} +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ .Release.Name }}-controller +subjects: + - kind: ServiceAccount + name: {{ .Release.Name }}-controller + namespace: {{ .Release.Namespace }} +roleRef: + kind: ClusterRole + name: {{ .Release.Name }}-controller + apiGroup: rbac.authorization.k8s.io +--- +{{ $releaseName := .Release.Name }} +kind: Deployment +apiVersion: apps/v1 +metadata: + name: {{ .Release.Name }}-controller + namespace: {{ .Release.Namespace }} +spec: + {{- if lt (.Values.controller.controllerCount | toString | atoi ) 1 -}} + {{- fail "value for .Values.controller.controllerCount should be atleast 1" }} + {{- else }} + replicas: {{ required "Must provide the number of controller instances to create." .Values.controller.controllerCount }} + {{- end }} + selector: + matchLabels: + app: {{ .Release.Name }}-controller + template: + metadata: + annotations: + kubectl.kubernetes.io/default-container: "driver" + labels: + app: {{ .Release.Name }}-controller + spec: + serviceAccountName: {{ .Release.Name }}-controller + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: app + operator: In + values: + - {{ .Release.Name }}-controller + topologyKey: "kubernetes.io/hostname" + {{- if .Values.controller.nodeSelector }} + nodeSelector: + {{- toYaml .Values.controller.nodeSelector | nindent 8 }} + {{- end }} + {{- if .Values.controller.tolerations }} + tolerations: + {{- toYaml .Values.controller.tolerations | nindent 6 }} + {{- end }} + containers: +{{- if .Values.podmon.enabled }} + - name: podmon + imagePullPolicy: {{ .Values.imagePullPolicy }} + image: {{ required "Must provide the podmon container image." .Values.images.podmon.image }} + args: + {{- toYaml .Values.podmon.controller.args | nindent 12 }} + env: + - name: MY_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: MY_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: MY_POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + - name: unity-config + mountPath: /unity-config +{{- end }} + - name: attacher + image: {{ required "Must provide the CSI attacher container image." .Values.images.attacher.image }} + args: + - "--csi-address=$(ADDRESS)" + - "--v=5" + - "--leader-election" + env: + - name: ADDRESS + value: /var/run/csi/csi.sock + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + - name: provisioner + image: {{ required "Must provide the CSI provisioner container image." .Values.images.provisioner.image }} + args: + - "--csi-address=$(ADDRESS)" + - "--volume-name-prefix={{ required "Must provide a Volume Name Prefix." .Values.controller.volumeNamePrefix }}" + - "--volume-name-uuid-length=10" + - "--timeout=180s" + - "--worker-threads=6" + - "--v=5" + - "--feature-gates=Topology=true" + - "--strict-topology=true" + - "--leader-election" + - "--leader-election-namespace={{ .Release.Namespace }}" + - "--default-fstype={{ .Values.defaultFsType | default "ext4" }}" + - "--enable-capacity={{ (include "csi-unity.isStorageCapacitySupported" .) | default false }}" + - "--capacity-ownerref-level=2" + - "--capacity-poll-interval={{ .Values.storageCapacity.pollInterval | default "5m" }}" + env: + - name: ADDRESS + value: /var/run/csi/csi.sock + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + {{- if hasKey .Values.controller "snapshot" }} + {{- if eq .Values.controller.snapshot.enabled true }} + - name: snapshotter + image: {{ required "Must provide the CSI snapshotter container image. " .Values.images.snapshotter.image }} + args: + - "--csi-address=$(ADDRESS)" + - "--snapshot-name-prefix={{ required "Must privided a Snapshot Name Prefix" .Values.controller.snapshot.snapNamePrefix }}" + - "--snapshot-name-uuid-length=10" + - "--timeout=360s" + - "--v=5" + - "--leader-election" + env: + - name: ADDRESS + value: /var/run/csi/csi.sock + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + {{- end}} + {{- end}} + {{- if hasKey .Values.controller "resizer" }} + {{- if eq .Values.controller.resizer.enabled true }} + - name: resizer + image: {{ required "Must provide the CSI resizer container image." .Values.images.resizer.image }} + args: + - "--csi-address=$(ADDRESS)" + - "--v=5" + - "--leader-election" + env: + - name: ADDRESS + value: /var/run/csi/csi.sock + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + {{ end }} + {{ end }} + {{- if hasKey .Values.controller "healthMonitor" }} + {{- if eq .Values.controller.healthMonitor.enabled true }} + - name: csi-external-health-monitor-controller + image: {{ required "Must provide the CSI external health monitor image." .Values.images.healthmonitor.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + - "--v=5" + - "--csi-address=$(ADDRESS)" + - "--leader-election" + - "--http-endpoint=:8080" + - "--enable-node-watcher=true" + - "--monitor-interval={{ .Values.controller.healthMonitor.interval | default "60s" }}" + - "--timeout=180s" + env: + - name: ADDRESS + value: /var/run/csi/csi.sock + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + {{- end }} + {{- end }} + - name: driver + image: "{{ required "Must provide the driver image repository." .Values.images.driver.image }}" + args: + - "--driver-name=csi-unity.dellemc.com" + - "--driver-config=/unity-config/driver-config-params.yaml" + - "--driver-secret=/unity-secret/config" + imagePullPolicy: {{ .Values.imagePullPolicy }} + env: + - name: CSI_ENDPOINT + value: /var/run/csi/csi.sock + - name: X_CSI_MODE + value: controller + - name: X_CSI_UNITY_AUTOPROBE + value: "true" + - name: SSL_CERT_DIR + value: /certs + {{- if hasKey .Values.controller "healthMonitor" }} + {{- if eq .Values.controller.healthMonitor.enabled true }} + - name: X_CSI_HEALTH_MONITOR_ENABLED + value: "{{ .Values.controller.healthMonitor.enabled }}" + {{- end }} + {{- end }} + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + - name: certs + mountPath: /certs + readOnly: true + - name: unity-config + mountPath: /unity-config + - name: unity-secret + mountPath: /unity-secret + volumes: + - name: certs + projected: + sources: +{{- range $i, $e := until (int .Values.certSecretCount ) }} + - secret: + name: {{ print $releaseName "-certs-" $e }} + items: + - key: cert-{{ $e }} + path: cert-{{ $e }} +{{- end }} + - name: socket-dir + emptyDir: + - name: unity-config + configMap: + name: {{ .Release.Name }}-config-params + - name: unity-secret + secret: + secretName: {{ .Release.Name }}-creds diff --git a/charts/dell/csi-unity/2.13.0/templates/csidriver.yaml b/charts/dell/csi-unity/2.13.0/templates/csidriver.yaml new file mode 100644 index 0000000000..d743c1da86 --- /dev/null +++ b/charts/dell/csi-unity/2.13.0/templates/csidriver.yaml @@ -0,0 +1,14 @@ +apiVersion: storage.k8s.io/v1 +kind: CSIDriver +metadata: + name: csi-unity.dellemc.com + labels: + security.openshift.io/csi-ephemeral-volume-profile: restricted +spec: + storageCapacity: {{ (include "csi-unity.isStorageCapacitySupported" .) | default false }} + attachRequired: true + podInfoOnMount: true + volumeLifecycleModes: + - Persistent + - Ephemeral + fsGroupPolicy: {{ .Values.fsGroupPolicy }} diff --git a/charts/dell/csi-unity/2.13.0/templates/driver-config-params.yaml b/charts/dell/csi-unity/2.13.0/templates/driver-config-params.yaml new file mode 100644 index 0000000000..2bbf894829 --- /dev/null +++ b/charts/dell/csi-unity/2.13.0/templates/driver-config-params.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Release.Name }}-config-params + namespace: {{ .Release.Namespace }} +data: + driver-config-params.yaml: | + CSI_LOG_LEVEL: "{{ .Values.logLevel }}" + ALLOW_RWO_MULTIPOD_ACCESS: "{{ .Values.allowRWOMultiPodAccess }}" + MAX_UNITY_VOLUMES_PER_NODE: "{{ .Values.maxUnityVolumesPerNode }}" + SYNC_NODE_INFO_TIME_INTERVAL: "{{ .Values.syncNodeInfoInterval }}" + TENANT_NAME: "{{ .Values.tenantName }}" + {{ if .Values.podmon.enabled }} + PODMON_CONTROLLER_LOG_LEVEL: "{{ .Values.logLevel }}" + PODMON_CONTROLLER_LOG_FORMAT: "TEXT" + PODMON_NODE_LOG_LEVEL: "{{ .Values.logLevel }}" + PODMON_NODE_LOG_FORMAT: "TEXT" + {{ end }} diff --git a/charts/dell/csi-unity/2.13.0/templates/node.yaml b/charts/dell/csi-unity/2.13.0/templates/node.yaml new file mode 100644 index 0000000000..a724f8fd46 --- /dev/null +++ b/charts/dell/csi-unity/2.13.0/templates/node.yaml @@ -0,0 +1,283 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Release.Name }}-node + namespace: {{ .Release.Namespace }} +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ .Release.Name }}-node +rules: + - apiGroups: [ "" ] + resources: [ "persistentvolumes" ] + verbs: [ "create", "delete", "get", "list", "watch", "update" ] + - apiGroups: [ "" ] + resources: [ "persistentvolumesclaims" ] + verbs: [ "get", "list", "watch", "update" ] + - apiGroups: [ "" ] + resources: [ "events" ] + verbs: [ "get", "list", "watch", "create", "update", "patch" ] + - apiGroups: [ "" ] + resources: [ "nodes" ] + verbs: [ "get", "list", "watch", "update", "patch" ] + - apiGroups: [ "storage.k8s.io" ] + resources: [ "volumeattachments" ] + verbs: [ "get", "list", "watch", "update" ] + - apiGroups: [ "storage.k8s.io" ] + resources: [ "storageclasses" ] + verbs: [ "get", "list", "watch" ] + - apiGroups: [ "storage.k8s.io" ] + resources: [ "volumeattachments" ] + verbs: [ "get", "list", "watch", "update" ] + - apiGroups: [ "security.openshift.io" ] + resourceNames: [ "privileged" ] + resources: [ "securitycontextconstraints" ] + verbs: [ "use" ] +{{- if .Values.podmon.enabled }} + - apiGroups: [ "" ] + resources: [ "pods" ] + verbs: [ "get", "list", "watch", "update", "delete" ] + - apiGroups: [ "coordination.k8s.io" ] + resources: [ "leases" ] + verbs: [ "get", "watch", "list", "delete", "update", "create" ] +{{- end }} +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ .Release.Name }}-node +subjects: + - kind: ServiceAccount + name: {{ .Release.Name }}-node + namespace: {{ .Release.Namespace }} +roleRef: + kind: ClusterRole + name: {{ .Release.Name }}-node + apiGroup: rbac.authorization.k8s.io +--- +{{ $releaseName := .Release.Name }} +kind: DaemonSet +apiVersion: apps/v1 +metadata: + name: {{ .Release.Name }}-node + namespace: {{ .Release.Namespace }} +spec: + updateStrategy: + type: RollingUpdate + selector: + matchLabels: + app: {{ .Release.Name }}-node + template: + metadata: + annotations: + kubectl.kubernetes.io/default-container: "driver" + labels: + app: {{ .Release.Name }}-node +{{- if .Values.podmon.enabled }} + driver.dellemc.com: dell-storage +{{- end }} + spec: + serviceAccountName: {{ .Release.Name }}-node + {{- if .Values.node.nodeSelector }} + nodeSelector: + {{- toYaml .Values.node.nodeSelector | nindent 8 }} + {{- end }} + {{- if .Values.node.tolerations }} + tolerations: + {{- toYaml .Values.node.tolerations | nindent 8 }} + {{- end }} + hostIPC: true + hostNetwork: true + dnsPolicy: {{ .Values.node.dnsPolicy }} + containers: +{{- if .Values.podmon.enabled }} + - name: podmon + securityContext: + privileged: true + capabilities: + add: ["SYS_ADMIN"] + allowPrivilegeEscalation: true + imagePullPolicy: {{ .Values.imagePullPolicy }} + image: {{ required "Must provide the podmon container image." .Values.images.podmon.image }} + args: + {{- toYaml .Values.podmon.node.args | nindent 12 }} + env: + - name: KUBE_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + - name: X_CSI_PRIVATE_MOUNT_DIR + value: "{{ .Values.kubeletConfigDir }}/plugins/unity.emc.dell.com/disks" + - name: MY_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: MY_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: MY_POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + volumeMounts: + - name: kubelet-pods + mountPath: {{ .Values.kubeletConfigDir }}/pods + mountPropagation: "Bidirectional" + - name: driver-path + mountPath: {{ .Values.kubeletConfigDir }}/plugins/unity.emc.dell.com + mountPropagation: "Bidirectional" + - name: volumedevices-path + mountPath: {{ .Values.kubeletConfigDir }}/plugins/kubernetes.io/csi + mountPropagation: "Bidirectional" + - name: dev + mountPath: /dev + - name: usr-bin + mountPath: /usr-bin + - name: var-run + mountPath: /var/run + - name: unity-config + mountPath: /unity-config +{{- end }} + - name: driver + securityContext: + privileged: true + capabilities: + add: ["SYS_ADMIN"] + allowPrivilegeEscalation: true + image: "{{ required "Must provide the driver image repository." .Values.images.driver.image }}" + args: + - "--driver-name=csi-unity.dellemc.com" + - "--driver-config=/unity-config/driver-config-params.yaml" + - "--driver-secret=/unity-secret/config" + imagePullPolicy: {{ .Values.imagePullPolicy }} + env: + - name: CSI_ENDPOINT + value: {{ .Values.kubeletConfigDir }}/plugins/unity.emc.dell.com/csi_sock + - name: X_CSI_MODE + value: node + - name: X_CSI_UNITY_AUTOPROBE + value: "true" + - name: X_CSI_UNITY_ALLOW_MULTI_POD_ACCESS + value: {{ .Values.allowRWOMultiPodAccess | default "false" | lower | quote }} + - name: X_CSI_ALLOWED_NETWORKS + value: "{{ .Values.allowedNetworks }}" + - name: X_CSI_PRIVATE_MOUNT_DIR + value: "{{ .Values.kubeletConfigDir }}/plugins/unity.emc.dell.com/disks" + - name: X_CSI_EPHEMERAL_STAGING_PATH + value: "{{ .Values.kubeletConfigDir }}/plugins/kubernetes.io/csi/pv/" + - name: X_CSI_ISCSI_CHROOT + value: {{ .Values.ISCSIChroot | default "/noderoot" }} + - name: X_CSI_UNITY_NODENAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + - name: X_CSI_UNITY_NODENAME_PREFIX + value: {{ .Values.nodeNamePrefix }} + - name: SSL_CERT_DIR + value: /certs + - name: X_CSI_UNITY_SYNC_NODEINFO_INTERVAL + value: {{ .Values.syncNodeInfoInterval | default "15" | quote }} + {{- if hasKey .Values.node "healthMonitor" }} + {{- if eq .Values.node.healthMonitor.enabled true }} + - name: X_CSI_HEALTH_MONITOR_ENABLED + value: "{{ .Values.node.healthMonitor.enabled }}" + {{- end }} + {{- end }} + volumeMounts: + - name: driver-path + mountPath: {{ .Values.kubeletConfigDir }}/plugins/unity.emc.dell.com + - name: volumedevices-path + mountPath: {{ .Values.kubeletConfigDir }}/plugins/kubernetes.io/csi + mountPropagation: "Bidirectional" + - name: pods-path + mountPath: {{ .Values.kubeletConfigDir }}/pods + mountPropagation: "Bidirectional" + - name: dev + mountPath: /dev + - name: noderoot + mountPath: /noderoot + - name: certs + mountPath: /certs + readOnly: true + - name: unity-config + mountPath: /unity-config + - name: unity-secret + mountPath: /unity-secret + - name: registrar + image: {{ required "Must provide the CSI registrar container image." .Values.images.registrar.image }} + args: + - "--v=5" + - "--csi-address=$(ADDRESS)" + - --kubelet-registration-path={{ .Values.kubeletConfigDir }}/plugins/unity.emc.dell.com/csi_sock + env: + - name: ADDRESS + value: /csi/csi_sock + - name: KUBE_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + volumeMounts: + - name: registration-dir + mountPath: /registration + - name: driver-path + mountPath: /csi + volumes: + - name: registration-dir + hostPath: + path: {{ .Values.kubeletConfigDir }}/plugins_registry/ + type: DirectoryOrCreate + - name: driver-path + hostPath: + path: {{ .Values.kubeletConfigDir }}/plugins/unity.emc.dell.com + type: DirectoryOrCreate + - name: volumedevices-path + hostPath: + path: {{ .Values.kubeletConfigDir }}/plugins/kubernetes.io/csi + type: DirectoryOrCreate + - name: pods-path + hostPath: + path: {{ .Values.kubeletConfigDir }}/pods + type: Directory + - name: dev + hostPath: + path: /dev + type: Directory + - name: noderoot + hostPath: + path: / + type: Directory + - name: certs + projected: + sources: +{{- range $i, $e := until (int .Values.certSecretCount ) }} + - secret: + name: {{ print $releaseName "-certs-" $e }} + items: + - key: cert-{{ $e }} + path: cert-{{ $e }} +{{- end }} + - name: unity-config + configMap: + name: {{ .Release.Name }}-config-params + - name: unity-secret + secret: + secretName: {{ .Release.Name }}-creds +{{- if .Values.podmon.enabled }} + - name: usr-bin + hostPath: + path: /usr/bin + type: Directory + - name: kubelet-pods + hostPath: + path: {{ .Values.kubeletConfigDir }}/pods + type: Directory + - name: var-run + hostPath: + path: /var/run + type: Directory +{{- end }} diff --git a/charts/dell/csi-unity/2.13.0/values.yaml b/charts/dell/csi-unity/2.13.0/values.yaml new file mode 100644 index 0000000000..a681e84aeb --- /dev/null +++ b/charts/dell/csi-unity/2.13.0/values.yaml @@ -0,0 +1,283 @@ +## K8S/DRIVER ATTRIBUTES +######################## + +# version: version of this values file +# Note: Do not change this value +# Examples : "v2.9.0" , "nightly" +version: "v2.13.0" + +images: + # "driver" defines the container image, used for the driver container. + driver: + image: quay.io/dell/container-storage-modules/csi-unity:v2.13.0 + # CSI sidecars + attacher: + image: registry.k8s.io/sig-storage/csi-attacher:v4.8.0 + provisioner: + image: registry.k8s.io/sig-storage/csi-provisioner:v5.1.0 + snapshotter: + image: registry.k8s.io/sig-storage/csi-snapshotter:v8.2.0 + resizer: + image: registry.k8s.io/sig-storage/csi-resizer:v1.13.1 + registrar: + image: registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.13.0 + healthmonitor: + image: registry.k8s.io/sig-storage/csi-external-health-monitor-controller:v0.14.0 + + # CSM sidecars + podmon: + image: quay.io/dell/container-storage-modules/podmon:v1.12.0 + +# LogLevel is used to set the logging level of the driver. +# Allowed values: "error", "warn"/"warning", "info", "debug" +# Default value: "info" +logLevel: "info" + +# certSecretCount: Represents number of certificate secrets, which user is going to create for +# ssl authentication. (unity-cert-0..unity-cert-n) +# Allowed values: n, where n > 0 +# Default value: None +certSecretCount: 1 + +# allowedNetworks: Custom networks for Unity export +# Specify list of networks which can be used for NFS I/O traffic; CIDR format should be used. +# Allowed values: list of one or more networks (comma separated) +# Default value: None +# Examples: 192.168.1.0/24, 192.168.100.0/22 +allowedNetworks: + +# imagePullPolicy: Policy to determine if the image should be pulled prior to starting the container. +# Allowed values: +# Always: Always pull the image. +# IfNotPresent: Only pull the image if it does not already exist on the node. +# Never: Never pull the image. +# Default value: IfNotPresent +imagePullPolicy: IfNotPresent + +# Specify kubelet config dir path. +# Ensure that the config.yaml file is present at this path. +# Default value: /var/lib/kubelet +kubeletConfigDir: /var/lib/kubelet + +# fsGroupPolicy: Defines if the underlying volume supports changing ownership and permission of the volume before being mounted. +# Allowed values: +# ReadWriteOnceWithFSType: supports volume ownership and permissions change only if the fsType is defined +# and the volume's accessModes contains ReadWriteOnce. +# File: kubernetes may use fsGroup to change permissions and ownership of the volume +# to match user requested fsGroup in the pod's security policy regardless of fstype or access mode. +# None: volumes will be mounted with no modifications. +# Default value: ReadWriteOnceWithFSType +fsGroupPolicy: ReadWriteOnceWithFSType + +# To set nodeSelectors and tolerations for controller. +# controller: configure controller pod specific parameters +controller: + # controllerCount: defines the number of csi-unity controller pods to deploy to + # the Kubernetes release. + # Allowed values: n, where n > 0 + # Default value: None + controllerCount: 2 + + # volumeNamePrefix: Prefix of PersistentVolume names created + # Allowed values: string + # Default value: csivol + # Examples: "k8s", "app1" + volumeNamePrefix: csivol + + snapshot: + # enabled: Enable/Disable volume snapshot feature + # Allowed values: + # true: enable volume snapshot feature(install snapshotter sidecar) + # false: disable volume snapshot feature(do not install snapshotter sidecar) + # Default value: None + enabled: true + + # snapNamePrefix: Prefix to apply to the names of a created snapshots + # Allowed values: string + # Default value: csi-snap + # Examples: "snap", "snapshot" + snapNamePrefix: csi-snap + + resizer: + # enabled: Enable/Disable volume expansion feature + # Allowed values: + # true: enable volume expansion feature(install resizer sidecar) + # false: disable volume snapshot feature(do not install resizer sidecar) + # Default value: None + enabled: true + + # nodeSelector: Define node selection constraints for controller pods. + # For the pod to be eligible to run on a node, the node must have each + # of the indicated key-value pairs as labels. + # Leave as blank to consider all nodes + # Allowed values: map of key-value pairs + # Default value: None + nodeSelector: + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/master taint + # node-role.kubernetes.io/master: "" + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/control-plane taint + # node-role.kubernetes.io/control-plane: "" + + # tolerations: Define tolerations for the controllers, if required. + # Leave as blank to install controller on worker nodes + # Default value: None + tolerations: + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/master taint + # - key: "node-role.kubernetes.io/master" + # operator: "Exists" + # effect: "NoExecute" + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/control-plane taint + # - key: "node-role.kubernetes.io/control-plane" + # operator: "Exists" + # effect: "NoSchedule" + + healthMonitor: + # enabled: Enable/Disable health monitor of CSI volumes- volume state, volume condition + # Allowed values: + # true: enable checking of health condition of CSI volumes + # false: disable checking of health condition of CSI volumes + # Default value: None + enabled: false + + # interval: Interval of monitoring volume health condition + # Allowed values: Number followed by unit of time (s,m,h) + # Default value: 60s + interval: 60s + +# node: configure node pod specific parameters +node: + # dnsPolicy : Define the DNS Policy of the Node service. + # ClusterFirstWithHostNet is the recommended and default DNS policy for the driver. + # Prior to v1.6 of the driver, the default DNS policy was ClusterFirst. + # In certain scenarios, users might need to change the default dnsPolicy. + # Default value: None + dnsPolicy: "ClusterFirstWithHostNet" + + healthMonitor: + # enabled: Enable/Disable health monitor of CSI Volumes - volume usage + # Allowed values: + # true: enable checking of health condition of CSI volumes + # false: disable checking of health condition of CSI volumes + # Default value: None + enabled: false + + # nodeSelector: Define node selection constraints for node pods. + # For the pod to be eligible to run on a node, the node must have each + # of the indicated key-value pairs as labels. + # Leave as blank to consider all nodes + # Allowed values: map of key-value pairs + # Default value: None + nodeSelector: + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/master taint + # node-role.kubernetes.io/master: "" + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/control-plane taint + # node-role.kubernetes.io/control-plane: "" + + # tolerations: Define tolerations for the node daemonset, if required. + # Default value: None + tolerations: + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/master taint + # - key: "node-role.kubernetes.io/master" + # operator: "Exists" + # effect: "NoExecute" + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/control-plane taint + # - key: "node-role.kubernetes.io/control-plane" + # operator: "Exists" + # effect: "NoExecute" + # - key: "node.kubernetes.io/memory-pressure" + # operator: "Exists" + # effect: "NoExecute" + # - key: "node.kubernetes.io/disk-pressure" + # operator: "Exists" + # effect: "NoExecute" + # - key: "node.kubernetes.io/network-unavailable" + # operator: "Exists" + # effect: "NoExecute" + # Uncomment if CSM for Resiliency and CSI Driver pods monitor are enabled + # - key: "offline.vxflexos.storage.dell.com" + # operator: "Exists" + # effect: "NoSchedule" + # - key: "vxflexos.podmon.storage.dell.com" + # operator: "Exists" + # effect: "NoSchedule" + # - key: "offline.unity.storage.dell.com" + # operator: "Exists" + # effect: "NoSchedule" + # - key: "unity.podmon.storage.dell.com" + # operator: "Exists" + # effect: "NoSchedule" + # - key: "offline.isilon.storage.dell.com" + # operator: "Exists" + # effect: "NoSchedule" + # - key: "isilon.podmon.storage.dell.com" + # operator: "Exists" + # effect: "NoSchedule" + +# CSM module attributes +# service to monitor failing jobs and notify +podmon: + # enabled - flag to enable or disable podmon + # allowed values : boolean + # defaule value : None + # Examples : true , false + enabled: false + controller: + args: + - "--csisock=unix:/var/run/csi/csi.sock" + - "--labelvalue=csi-unity" + - "--driverPath=csi-unity.dellemc.com" + - "--mode=controller" + - "--skipArrayConnectionValidation=false" + - "--driver-config-params=/unity-config/driver-config-params.yaml" + - "--driverPodLabelValue=dell-storage" + - "--ignoreVolumelessPods=false" + node: + args: + - "--csisock=unix:/var/lib/kubelet/plugins/unity.emc.dell.com/csi_sock" + - "--labelvalue=csi-unity" + - "--driverPath=csi-unity.dellemc.com" + - "--mode=node" + - "--leaderelection=false" + - "--driver-config-params=/unity-config/driver-config-params.yaml" + - "--driverPodLabelValue=dell-storage" + - "--ignoreVolumelessPods=false" + +### The below parameters have been discontinued for configuration from secret.yaml and will have to be configured only in values.yaml + +# syncNodeInfoInterval - Time interval to add node info to array. Default 15 minutes. Minimum value should be 1. +# Allowed values: integer +# Default value: 15 +# Examples : 0 , 2 +syncNodeInfoInterval: 15 + +# allowRWOMultiPodAccess - Flag to enable sharing of volumes across multiple pods within the same node in RWO access mode. +# Allowed values: boolean +# Default value: "false" +# Examples : "true" , "false" +allowRWOMultiPodAccess: "false" + +# maxUnityVolumesPerNode - Maximum number of volumes that controller can publish to the node. +# Allowed values: integer +# Default value: 0 +# Examples : 0 , 1 +maxUnityVolumesPerNode: 0 + +# tenantName - Tenant name that need to added while adding host entry to the array. +# Allowed values: string +# Default value: "" +# Examples : "tenant2" , "tenant3" +tenantName: "" + +# Storage Capacity Tracking +# Note: Capacity tracking is supported in kubernetes v1.24 and above, this feature will be automatically disabled in older versions. +storageCapacity: + # enabled : Enable/Disable storage capacity tracking + # Allowed values: + # true: enable storage capacity tracking + # false: disable storage capacity tracking + # Default value: true + enabled: true + # pollInterval : Configure how often external-provisioner polls the driver to detect changed capacity + # Allowed values: 1m,2m,3m,...,10m,...,60m etc + # Default value: 5m + pollInterval: 5m diff --git a/charts/dell/csi-vxflexos/2.13.0/Chart.yaml b/charts/dell/csi-vxflexos/2.13.0/Chart.yaml new file mode 100644 index 0000000000..35de8de9e2 --- /dev/null +++ b/charts/dell/csi-vxflexos/2.13.0/Chart.yaml @@ -0,0 +1,22 @@ +annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Dell CSI PowerFlex + catalog.cattle.io/kube-version: '>= 1.21.0' + catalog.cattle.io/namespace: vxflexos + catalog.cattle.io/release-name: vxflexos +apiVersion: v2 +appVersion: 2.13.0 +description: 'VxFlex OS CSI (Container Storage Interface) driver Kubernetes integration. + This chart includes everything required to provision via CSI as well as a VxFlex + OS StorageClass. ' +icon: file://assets/icons/csi-vxflexos.png +keywords: +- csi +- storage +kubeVersion: '>= 1.21.0' +maintainers: +- name: DellEMC +name: csi-vxflexos +sources: +- https://github.com/dell/csi-vxflexos +version: 2.13.0 diff --git a/charts/dell/csi-vxflexos/2.13.0/app-readme.md b/charts/dell/csi-vxflexos/2.13.0/app-readme.md new file mode 100644 index 0000000000..4c22667c1b --- /dev/null +++ b/charts/dell/csi-vxflexos/2.13.0/app-readme.md @@ -0,0 +1,10 @@ +CSI Driver for PowerFlex is part of the CSM (Container Storage Modules) open-source suite of Kubernetes storage enablers for Dell EMC products. CSI Driver for PowerFlex is a Container Storage Interface (CSI) driver that provides support for provisioning persistent storage using Dell EMC PowerFlex storage array. + +Pre-Install +- All nodes must have the SDC client installed to satisfy the CSI driver for PowerFlex. Ensure that nodes are provided at least two (2) interfaces and potentially up to four (4) interfaces depending on the PowerFlex system configuration. +- [Install Storage Data Client](https://dell.github.io/csm-docs/docs/csidriver/installation/helm/powerflex/#manual-sdc-deployment) +- [Create Configuration Secret (Step 4)](https://dell.github.io/csm-docs/docs/csidriver/installation/helm/powerflex/#install-the-driver) + +Post-Install +- [Create Storage Class(es)](https://dell.github.io/csm-docs/docs/csidriver/installation/helm/powerflex/#storage-classes) + [Storage Class Examples](https://github.com/dell/csi-powerflex/tree/main/samples/storageclass) diff --git a/charts/dell/csi-vxflexos/2.13.0/questions.yaml b/charts/dell/csi-vxflexos/2.13.0/questions.yaml new file mode 100644 index 0000000000..7c196f8bed --- /dev/null +++ b/charts/dell/csi-vxflexos/2.13.0/questions.yaml @@ -0,0 +1,21 @@ +categories: +- storage +namespace: vxflexos +labels: + io.rancher.certified: partner +questions: +- variable: defaultFsType + label: "Defines Filesystem format" + type: enum + required: true + group: "General settings (Required)" + options: + - "ext4" + - "xfs" + +- variable: volumeNamePrefix + label: "Defines a string prepended to each volume created" + type: string + default: "k8svol" + required: true + group: "General settings (Required)" diff --git a/charts/dell/csi-vxflexos/2.13.0/templates/_helpers.tpl b/charts/dell/csi-vxflexos/2.13.0/templates/_helpers.tpl new file mode 100644 index 0000000000..a7df6b372c --- /dev/null +++ b/charts/dell/csi-vxflexos/2.13.0/templates/_helpers.tpl @@ -0,0 +1,10 @@ +{{/* +Return true if storage capacity tracking is enabled and is supported based on k8s version +*/}} +{{- define "csi-vxflexos.isStorageCapacitySupported" -}} + {{- if eq .Values.storageCapacity.enabled true -}} + {{- if and (eq .Capabilities.KubeVersion.Major "1") (ge (trimSuffix "+" .Capabilities.KubeVersion.Minor) "24") -}} + {{- true -}} + {{- end -}} + {{- end -}} +{{- end -}} \ No newline at end of file diff --git a/charts/dell/csi-vxflexos/2.13.0/templates/controller.yaml b/charts/dell/csi-vxflexos/2.13.0/templates/controller.yaml new file mode 100644 index 0000000000..b4a69fe793 --- /dev/null +++ b/charts/dell/csi-vxflexos/2.13.0/templates/controller.yaml @@ -0,0 +1,476 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Release.Name }}-controller + namespace: {{ .Release.Namespace }} +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ .Release.Name }}-controller +rules: + - apiGroups: ["coordination.k8s.io"] + resources: ["leases"] + verbs: ["get", "watch", "list", "delete", "update", "create"] + - apiGroups: [""] + resources: ["events"] + verbs: ["list", "watch", "create", "update", "patch"] + - apiGroups: [""] + resources: ["nodes"] +{{- if hasKey .Values "podmon" }} +{{- if eq .Values.podmon.enabled true }} + verbs: ["get", "list", "watch", "patch"] +{{- else }} + verbs: ["get", "list", "watch"] +{{- end }} +{{- end }} + - apiGroups: [""] + resources: ["persistentvolumes"] + verbs: ["get", "list", "watch", "create", "delete", "update", "patch"] + - apiGroups: [""] + resources: ["persistentvolumeclaims"] + verbs: ["get", "list", "watch", "update", "patch"] + - apiGroups: [""] + resources: ["persistentvolumeclaims/status"] + verbs: ["update", "patch"] + - apiGroups: ["storage.k8s.io"] + resources: ["storageclasses"] + verbs: ["get", "list", "watch"] + - apiGroups: ["storage.k8s.io"] + resources: ["volumeattachments"] +{{- if hasKey .Values "podmon" }} +{{- if eq .Values.podmon.enabled true }} + verbs: ["get", "list", "watch", "update", "patch", "delete"] +{{- else }} + verbs: ["get", "list", "watch", "update", "patch"] +{{- end }} +{{- end }} + - apiGroups: ["storage.k8s.io"] + resources: ["csinodes"] + verbs: ["get", "list", "watch", "update"] + - apiGroups: ["storage.k8s.io"] + resources: ["volumeattachments/status"] + verbs: ["patch"] + - apiGroups: ["csi.storage.k8s.io"] + resources: ["csinodeinfos"] + verbs: ["get", "list", "watch"] + - apiGroups: [""] + resources: ["pods"] +{{- if hasKey .Values "podmon" }} +{{- if eq .Values.podmon.enabled true }} + verbs: ["get", "list", "watch", "update", "delete"] +{{- else }} + verbs: ["get", "list", "watch"] +{{- end }} +{{- end }} + - apiGroups: [""] + resources: ["configmaps"] + verbs: ["get", "update"] +# below for snapshotter + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "list"] +{{- if hasKey .Values "vgsnapshotter" }} +{{- if eq .Values.vgsnapshotter.enabled true }} + - apiGroups: ["volumegroup.storage.dell.com"] + resources: ["dellcsivolumegroupsnapshots","dellcsivolumegroupsnapshots/status"] + verbs: ["create", "list", "watch", "delete", "update"] +{{- end }} +{{- end }} + - apiGroups: ["snapshot.storage.k8s.io"] + resources: ["volumesnapshotclasses"] + verbs: ["get", "list", "watch"] + - apiGroups: ["snapshot.storage.k8s.io"] + resources: ["volumesnapshotcontents"] + verbs: ["create", "get", "list", "watch", "update", "delete", "patch"] + - apiGroups: ["snapshot.storage.k8s.io"] + resources: ["volumesnapshots"] +{{- if hasKey .Values "vgsnapshotter" }} +{{- if eq .Values.vgsnapshotter.enabled true }} + verbs: ["get", "list", "watch", "update", "create", "delete"] +{{- else }} + verbs: ["get", "list", "watch", "update"] +{{- end }} +{{- end }} + - apiGroups: ["snapshot.storage.k8s.io"] + resources: ["volumesnapshots/status","volumesnapshotcontents/status"] + verbs: ["get", "list", "watch", "update", "patch"] + - apiGroups: ["apiextensions.k8s.io"] + resources: ["customresourcedefinitions"] + verbs: ["create", "list", "watch", "delete", "update"] +{{- if hasKey .Values.controller "replication" }} +{{- if eq .Values.controller.replication.enabled true}} + - apiGroups: ["replication.storage.dell.com"] + resources: ["dellcsireplicationgroups"] + verbs: ["create", "delete", "get", "list", "patch", "update", "watch"] + - apiGroups: ["replication.storage.dell.com"] + resources: ["dellcsireplicationgroups/status"] + verbs: ["get", "patch", "update"] + - apiGroups: [""] + resources: ["configmaps"] + verbs: ["create", "delete", "get", "list", "watch", "update", "patch"] + - apiGroups: [""] + resources: ["namespaces"] + verbs: ["create", "get", "list", "watch"] +{{- end}} +{{- end}} +# Permissions for CSIStorageCapacity +{{- if eq (include "csi-vxflexos.isStorageCapacitySupported" .) "true" }} + - apiGroups: ["storage.k8s.io"] + resources: ["csistoragecapacities"] + verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] + - apiGroups: [""] + resources: ["pods"] + verbs: ["get"] + - apiGroups: ["apps"] + resources: ["replicasets"] + verbs: ["get"] +{{- end }} +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ .Release.Name }}-controller +subjects: + - kind: ServiceAccount + name: {{ .Release.Name }}-controller + namespace: {{ .Release.Namespace }} +roleRef: + kind: ClusterRole + name: {{ .Release.Name }}-controller + apiGroup: rbac.authorization.k8s.io +--- +kind: Deployment +apiVersion: apps/v1 +metadata: + name: {{ .Release.Name }}-controller + namespace: {{ .Release.Namespace }} + {{- if hasKey .Values "authorization" }} + {{- if eq .Values.authorization.enabled true }} + annotations: + com.dell.karavi-authorization-proxy: "true" + {{ end }} + {{ end }} +spec: +{{- if gt (int .Values.controller.controllerCount) 1 }} + strategy: + rollingUpdate: + maxUnavailable: 1 +{{- end }} + selector: + matchLabels: + name: {{ .Release.Name }}-controller + replicas: {{ required "Must provide the number of controller instances to create." .Values.controller.controllerCount }} + template: + metadata: + labels: + {{- if eq (.Values.vgsnapshotter.enabled| toString) "true" }} + vg-snapshotter-enabled: "true" + {{- else if eq (.Values.vgsnapshotter.enabled| toString) "false" }} + vg-snapshotter-enabled: "false" + {{- end }} + name: {{ .Release.Name }}-controller + annotations: + kubectl.kubernetes.io/default-container: "driver" + spec: + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: name + operator: In + values: + - {{ .Release.Name }}-controller + topologyKey: kubernetes.io/hostname + serviceAccountName: {{ .Release.Name }}-controller + {{- if .Values.controller.nodeSelector }} + nodeSelector: + {{- toYaml .Values.controller.nodeSelector | nindent 8 }} + {{- end }} + {{- if .Values.controller.tolerations }} + tolerations: + {{- toYaml .Values.controller.tolerations | nindent 6 }} + {{- end }} + containers: +{{- if hasKey .Values "podmon" }} +{{- if eq .Values.podmon.enabled true }} + - name: podmon + image: {{ required "Must provide the podmon container image." .Values.images.podmon.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + {{- toYaml .Values.podmon.controller.args | nindent 12 }} + env: + - name: MY_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: MY_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: MY_POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + - name: vxflexos-config-params + mountPath: /vxflexos-config-params +{{- end }} +{{- end }} + - name: attacher + image: {{ required "Must provide the CSI attacher container image." .Values.images.attacher.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + - "--csi-address=$(ADDRESS)" + - "--v=5" + - "--leader-election=true" + env: + - name: ADDRESS + value: /var/run/csi/csi.sock + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + {{- if hasKey .Values.controller "replication" }} + {{- if eq .Values.controller.replication.enabled true}} + - name: dell-csi-replicator + image: {{ required "Must provide the Dell CSI Replicator image." .Values.images.replication.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + - "--csi-address=$(ADDRESS)" + - "--leader-election=true" + - "--worker-threads=2" + - "--retry-interval-start=1s" + - "--retry-interval-max=300s" + - "--timeout=300s" + - "--context-prefix={{ .Values.controller.replication.replicationContextPrefix}}" + - "--prefix={{ .Values.controller.replication.replicationPrefix}}" + env: + - name: ADDRESS + value: /var/run/csi/csi.sock + - name: X_CSI_REPLICATION_CONFIG_DIR + value: /vxflexos-config-params + - name: X_CSI_REPLICATION_CONFIG_FILE_NAME + value: driver-config-params.yaml + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + - name: vxflexos-config-params + mountPath: /vxflexos-config-params + {{- end }} + {{- end }} + - name: provisioner + image: {{ required "Must provide the CSI provisioner container image." .Values.images.provisioner.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + - "--csi-address=$(ADDRESS)" + - "--feature-gates=Topology=true" + - "--volume-name-prefix={{ required "Must provide a value to prefix to driver created volume names" .Values.controller.volumeNamePrefix }}" + - "--volume-name-uuid-length=10" + - "--leader-election=true" + - "--timeout=120s" + - "--v=5" + - "--default-fstype={{ .Values.defaultFsType | default "ext4" }}" + - "--extra-create-metadata" + - "--enable-capacity={{ (include "csi-vxflexos.isStorageCapacitySupported" .) | default false }}" + - "--capacity-ownerref-level=2" + - "--capacity-poll-interval={{ .Values.storageCapacity.pollInterval | default "5m" }}" + env: + - name: ADDRESS + value: /var/run/csi/csi.sock + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + {{- if hasKey .Values.controller "healthMonitor" }} + {{- if eq .Values.controller.healthMonitor.enabled true}} + - name: csi-external-health-monitor-controller + image: {{ required "Must provide the CSI external health monitor image." .Values.images.healthmonitor.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + - "--csi-address=$(ADDRESS)" + - "--v=5" + - "--leader-election=true" + - "--enable-node-watcher=true" + - "--http-endpoint=:8080" + - "--monitor-interval={{ .Values.controller.healthMonitor.interval | default "60s" }}" + - "--timeout=180s" + env: + - name: ADDRESS + value: /var/run/csi/csi.sock + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + {{- end }} + {{- end }} +{{- if hasKey .Values "vgsnapshotter" }} +{{- if eq .Values.vgsnapshotter.enabled true }} + - name: vg-snapshotter + image: {{ required "Must provide the vgsnapshotter container image." .Values.images.vgsnapshotter.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + env: + - name: ADDRESS + value: /var/run/csi/csi.sock + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi +{{- end }} +{{- end }} +{{- if hasKey .Values.controller "snapshot" }} +{{- if eq .Values.controller.snapshot.enabled true }} + - name: snapshotter + image: {{ required "Must provide the CSI snapshotter container image. " .Values.images.snapshotter.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + - "--csi-address=$(ADDRESS)" + - "--timeout=120s" + - "--v=5" + - "--leader-election=true" + env: + - name: ADDRESS + value: /var/run/csi/csi.sock + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi +{{- end }} +{{- end }} +{{- if hasKey .Values.controller "resizer" }} +{{- if eq .Values.controller.resizer.enabled true }} + - name: resizer + image: {{ required "Must provide the CSI resizer container image." .Values.images.resizer.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + - "--csi-address=$(ADDRESS)" + - "--v=5" + - "--leader-election=true" + env: + - name: ADDRESS + value: /var/run/csi/csi.sock + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi +{{- end }} +{{- end }} +{{- if hasKey .Values "authorization" }} +{{- if eq .Values.authorization.enabled true }} + - name: karavi-authorization-proxy + image: {{ required "Must provide the authorization sidecar container image." .Values.images.authorization.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + env: + - name: PROXY_HOST + value: "{{ .Values.authorization.proxyHost }}" + - name: SKIP_CERTIFICATE_VALIDATION + value: "{{ .Values.authorization.skipCertificateValidation }}" + - name: PLUGIN_IDENTIFIER + value: powerflex + - name: ACCESS_TOKEN + valueFrom: + secretKeyRef: + name: proxy-authz-tokens + key: access + - name: REFRESH_TOKEN + valueFrom: + secretKeyRef: + name: proxy-authz-tokens + key: refresh + volumeMounts: + - name: karavi-authorization-config + mountPath: /etc/karavi-authorization/config + - name: proxy-server-root-certificate + mountPath: /etc/karavi-authorization/root-certificates + - name: vxflexos-config-params + mountPath: /etc/karavi-authorization +{{- end }} +{{- end }} + - name: driver + image: "{{ required "Must provide the driver image repository." .Values.images.driver.image }}" + imagePullPolicy: {{ .Values.imagePullPolicy }} + command: [ "/csi-vxflexos.sh" ] + args: + - "--array-config=/vxflexos-config/config" + - "--driver-config-params=/vxflexos-config-params/driver-config-params.yaml" + env: + - name: CSI_ENDPOINT + value: /var/run/csi/csi.sock + - name: X_CSI_MODE + value: controller + - name: X_CSI_VXFLEXOS_ENABLESNAPSHOTCGDELETE + value: "{{ required "Enable this to allow deletion of all snaps in CG" .Values.enablesnapshotcgdelete }}" + - name: X_CSI_VXFLEXOS_ENABLELISTVOLUMESNAPSHOT + value: "{{ required "Enable this to have CSI ListVolumes include snapshots" .Values.enablelistvolumesnapshot }}" + - name: SSL_CERT_DIR + value: /certs + {{- if hasKey .Values.controller "replication" }} + {{- if eq .Values.controller.replication.enabled true}} + - name: X_CSI_REPLICATION_CONTEXT_PREFIX + value: {{ .Values.controller.replication.replicationContextPrefix | default "powerflex"}} + - name: X_CSI_REPLICATION_PREFIX + value: {{ .Values.controller.replication.replicationPrefix | default "replication.storage.dell.com"}} + {{- end }} + {{- end }} + {{- if hasKey .Values.controller "healthMonitor" }} + {{- if eq .Values.controller.healthMonitor.enabled true}} + - name: X_CSI_HEALTH_MONITOR_ENABLED + value: "{{ .Values.controller.healthMonitor.enabled }}" + {{- end }} + {{- end }} + {{- if hasKey .Values "enableQuota" }} + {{- if eq .Values.enableQuota true}} + - name: X_CSI_QUOTA_ENABLED + value: "{{ .Values.enableQuota }}" + {{- end }} + {{- end }} + - name: X_CSI_POWERFLEX_EXTERNAL_ACCESS + value: {{ .Values.externalAccess }} + volumeMounts: + - name: socket-dir + mountPath: /var/run/csi + - name: vxflexos-config + mountPath: /vxflexos-config + - name: vxflexos-config-params + mountPath: /vxflexos-config-params +{{- if ge (int .Values.certSecretCount) 1 }} + - name: certs + mountPath: /certs + readOnly: true +{{- end}} + volumes: + - name: socket-dir + emptyDir: + - name: vxflexos-config + secret: + secretName: {{ .Release.Name }}-config + - name: vxflexos-config-params + configMap: + name: {{ .Release.Name }}-config-params + {{- if hasKey .Values "authorization" }} + {{- if eq .Values.authorization.enabled true }} + - name: karavi-authorization-config + secret: + secretName: karavi-authorization-config + - name: proxy-server-root-certificate + secret: + secretName: proxy-server-root-certificate + {{ end }} + {{ end }} +{{- if ge (int .Values.certSecretCount) 1 }} + - name: certs + projected: + sources: +{{- range $i, $e := until (int .Values.certSecretCount ) }} + - secret: + name: {{ print $.Release.Name "-certs-" $e }} + items: + - key: cert-{{ $e }} + path: cert-{{ $e }} +{{- end }} +{{- end }} diff --git a/charts/dell/csi-vxflexos/2.13.0/templates/csidriver.yaml b/charts/dell/csi-vxflexos/2.13.0/templates/csidriver.yaml new file mode 100644 index 0000000000..2dbf1f840b --- /dev/null +++ b/charts/dell/csi-vxflexos/2.13.0/templates/csidriver.yaml @@ -0,0 +1,14 @@ +apiVersion: storage.k8s.io/v1 +kind: CSIDriver +metadata: + name: csi-vxflexos.dellemc.com + labels: + security.openshift.io/csi-ephemeral-volume-profile: restricted +spec: + storageCapacity: {{ (include "csi-vxflexos.isStorageCapacitySupported" .) | default false }} + fsGroupPolicy: {{ .Values.fsGroupPolicy }} + attachRequired: true + podInfoOnMount: true + volumeLifecycleModes: + - Persistent + - Ephemeral diff --git a/charts/dell/csi-vxflexos/2.13.0/templates/driver-config-params.yaml b/charts/dell/csi-vxflexos/2.13.0/templates/driver-config-params.yaml new file mode 100644 index 0000000000..72c0d73be9 --- /dev/null +++ b/charts/dell/csi-vxflexos/2.13.0/templates/driver-config-params.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Release.Name }}-config-params + namespace: {{ .Release.Namespace }} +data: + driver-config-params.yaml: | + CSI_LOG_LEVEL: "{{ .Values.logLevel }}" + CSI_LOG_FORMAT: "{{ .Values.logFormat }}" + {{ if .Values.podmon.enabled }} + PODMON_CONTROLLER_LOG_LEVEL: "{{ .Values.logLevel }}" + PODMON_CONTROLLER_LOG_FORMAT: "{{ .Values.logFormat }}" + PODMON_NODE_LOG_LEVEL: "{{ .Values.logLevel }}" + PODMON_NODE_LOG_FORMAT: "{{ .Values.logFormat }}" + {{ end }} + interfaceNames: + {{- range $node, $interfaces := .Values.interfaceNames }} + {{ $node }}: "{{ $interfaces }}" + {{- end }} + diff --git a/charts/dell/csi-vxflexos/2.13.0/templates/node.yaml b/charts/dell/csi-vxflexos/2.13.0/templates/node.yaml new file mode 100644 index 0000000000..57518602ba --- /dev/null +++ b/charts/dell/csi-vxflexos/2.13.0/templates/node.yaml @@ -0,0 +1,441 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Release.Name }}-node + namespace: {{ .Release.Namespace }} +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ .Release.Name }}-node +rules: + - apiGroups: [""] + resources: ["persistentvolumes"] + verbs: ["create", "delete", "get", "list", "watch", "update"] + - apiGroups: [""] + resources: ["persistentvolumesclaims"] + verbs: ["get", "list", "watch", "update"] + - apiGroups: [""] + resources: ["events"] + verbs: ["get", "list", "watch", "create", "update", "patch"] + - apiGroups: [""] + resources: ["nodes"] + verbs: ["get", "list", "watch", "update", "patch"] + - apiGroups: ["storage.k8s.io"] + resources: ["volumeattachments"] + verbs: ["get", "list", "watch", "update"] + - apiGroups: ["storage.k8s.io"] + resources: ["storageclasses"] + verbs: ["get", "list", "watch"] + - apiGroups: ["storage.k8s.io"] + resources: ["volumeattachments"] + verbs: ["get", "list", "watch", "update"] + - apiGroups: ["security.openshift.io"] + resourceNames: ["privileged"] + resources: ["securitycontextconstraints"] + verbs: ["use"] +{{- if hasKey .Values "podmon" }} +{{- if eq .Values.podmon.enabled true }} + - apiGroups: [""] + resources: ["pods"] + verbs: ["get", "list", "watch", "update", "delete"] + - apiGroups: ["coordination.k8s.io"] + resources: ["leases"] + verbs: ["get", "watch", "list", "delete", "update", "create"] +{{ end }} +{{ end }} + - apiGroups: [""] + resources: ["configmaps"] + verbs: ["get", "update"] +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ .Release.Name }}-node +subjects: + - kind: ServiceAccount + name: {{ .Release.Name }}-node + namespace: {{ .Release.Namespace }} +roleRef: + kind: ClusterRole + name: {{ .Release.Name }}-node + apiGroup: rbac.authorization.k8s.io +--- +kind: DaemonSet +apiVersion: apps/v1 +metadata: + name: {{ .Release.Name }}-node + namespace: {{ .Release.Namespace }} + {{- if hasKey .Values "authorization" }} + {{- if eq .Values.authorization.enabled true }} + annotations: + com.dell.karavi-authorization-proxy: "true" + {{ end }} + {{ end }} +spec: + selector: + matchLabels: + app: {{ .Release.Name }}-node + template: + metadata: + labels: + app: {{ .Release.Name }}-node +{{- if eq .Values.podmon.enabled true }} + driver.dellemc.com: dell-storage +{{- end }} + annotations: + kubectl.kubernetes.io/default-container: "driver" + spec: + {{- if .Values.node.nodeSelector }} + nodeSelector: + {{- toYaml .Values.node.nodeSelector | nindent 8 }} + {{- end }} + {{- if .Values.node.tolerations }} + tolerations: + {{- toYaml .Values.node.tolerations | nindent 6 }} + {{- end }} + serviceAccount: {{ .Release.Name }}-node + dnsPolicy: ClusterFirstWithHostNet + hostNetwork: true + {{- if and .Values.monitor.enabled .Values.monitor.hostPID }} + hostPID: true + {{- else }} + hostPID: false + {{- end }} + containers: +{{- if hasKey .Values "podmon" }} +{{- if eq .Values.podmon.enabled true }} + - name: podmon + securityContext: + privileged: true + capabilities: + add: ["SYS_ADMIN"] + allowPrivilegeEscalation: true + image: {{ required "Must provide the podmon container image." .Values.images.podmon.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + {{- toYaml .Values.podmon.node.args | nindent 12 }} + env: + - name: KUBE_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + - name: X_CSI_PRIVATE_MOUNT_DIR + value: "{{ .Values.kubeletConfigDir }}/plugins/vxflexos.emc.dell.com/disks" + - name: MY_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: MY_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: MY_POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + volumeMounts: + - name: kubelet-pods + mountPath: {{ .Values.kubeletConfigDir }}/pods + mountPropagation: "Bidirectional" + - name: driver-path + mountPath: {{ .Values.kubeletConfigDir }}/plugins/vxflexos.emc.dell.com + mountPropagation: "Bidirectional" + - name: usr-bin + mountPath: /usr-bin + - name: var-run + mountPath: /var/run + - name: vxflexos-config-params + mountPath: /vxflexos-config-params +{{- end }} +{{- end }} +{{- if hasKey .Values "authorization" }} +{{- if eq .Values.authorization.enabled true }} + - name: karavi-authorization-proxy + image: {{ required "Must provide the authorization sidecar container image." .Values.images.authorization.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + env: + - name: PROXY_HOST + value: "{{ .Values.authorization.proxyHost }}" + - name: SKIP_CERTIFICATE_VALIDATION + value: "{{ .Values.authorization.skipCertificateValidation }}" + - name: PLUGIN_IDENTIFIER + value: powerflex + - name: ACCESS_TOKEN + valueFrom: + secretKeyRef: + name: proxy-authz-tokens + key: access + - name: REFRESH_TOKEN + valueFrom: + secretKeyRef: + name: proxy-authz-tokens + key: refresh + volumeMounts: + - name: karavi-authorization-config + mountPath: /etc/karavi-authorization/config + - name: proxy-server-root-certificate + mountPath: /etc/karavi-authorization/root-certificates + - name: vxflexos-config-params + mountPath: /etc/karavi-authorization +{{ end }} +{{ end }} + - name: driver + securityContext: + privileged: true + allowPrivilegeEscalation: true + capabilities: + add: ["SYS_ADMIN"] + image: "{{ required "Must provide the driver image repository." .Values.images.driver.image }}" + imagePullPolicy: {{ .Values.imagePullPolicy }} + command: [ "/csi-vxflexos.sh" ] + args: + - "--array-config=/vxflexos-config/config" + - "--driver-config-params=/vxflexos-config-params/driver-config-params.yaml" + env: + - name: CSI_ENDPOINT + value: unix://{{ .Values.kubeletConfigDir }}/plugins/vxflexos.emc.dell.com/csi_sock + - name: X_CSI_MODE + value: node + - name: X_CSI_PRIVATE_MOUNT_DIR + value: "{{ .Values.kubeletConfigDir }}/plugins/vxflexos.emc.dell.com/disks" + - name: X_CSI_ALLOW_RWO_MULTI_POD_ACCESS + value: "{{ required "Must provide a true/false string to allow RWO multi pod access." .Values.allowRWOMultiPodAccess }}" + - name: X_CSI_MAX_VOLUMES_PER_NODE + value: "{{ .Values.maxVxflexosVolumesPerNode }}" + - name: SSL_CERT_DIR + value: /certs + {{- if hasKey .Values.node "healthMonitor" }} + - name: X_CSI_HEALTH_MONITOR_ENABLED + value: "{{ .Values.node.healthMonitor.enabled }}" + {{- end }} + {{- if hasKey .Values.node "approveSDC" }} + - name: X_CSI_APPROVE_SDC_ENABLED + value: "{{ .Values.node.approveSDC.enabled }}" + {{- end }} + {{- if hasKey .Values.node "renameSDC" }} + - name: X_CSI_RENAME_SDC_ENABLED + value: "{{ .Values.node.renameSDC.enabled }}" + {{- if eq .Values.node.renameSDC.enabled true }} + - name: X_CSI_RENAME_SDC_PREFIX + value: "{{ .Values.node.renameSDC.prefix }}" + {{- end }} + {{- end }} + - name: X_CSI_POWERFLEX_KUBE_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + volumeMounts: + - name: driver-path + mountPath: {{ .Values.kubeletConfigDir }}/plugins/vxflexos.emc.dell.com + - name: disks-path + mountPath: {{ .Values.kubeletConfigDir }}/plugins/vxflexos.emc.dell.com/disks + mountPropagation: "Bidirectional" + - name: volumedevices-path + mountPath: {{ .Values.kubeletConfigDir }}/plugins/kubernetes.io/csi/volumeDevices + mountPropagation: "Bidirectional" + - name: pods-path + mountPath: {{ .Values.kubeletConfigDir }}/pods + mountPropagation: "Bidirectional" + - name: noderoot + mountPath: /noderoot + - name: dev + mountPath: /dev + - name: vxflexos-config + mountPath: /vxflexos-config + - name: vxflexos-config-params + mountPath: /vxflexos-config-params +{{- if ge (int .Values.certSecretCount) 1 }} + - name: certs + mountPath: /certs + readOnly: true +{{- end}} + - name: registrar + image: {{ required "Must provide the CSI registrar container image." .Values.images.registrar.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + args: + - "--v=5" + - "--csi-address=$(ADDRESS)" + - --kubelet-registration-path={{ .Values.kubeletConfigDir }}/plugins/vxflexos.emc.dell.com/csi_sock + env: + - name: ADDRESS + value: /csi/csi_sock + - name: KUBE_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + volumeMounts: + - name: registration-dir + mountPath: /registration + - name: driver-path + mountPath: /csi + {{- if eq .Values.monitor.enabled true }} + - name: sdc-monitor + securityContext: + privileged: true + image: {{ required "Must provide the PowerFlex SDC container image." .Values.images.powerflexSdc.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + env: + {{ if eq .Values.monitor.hostPID true }} + - name: HOST_PID + value: "1" + {{ else }} + - name: HOST_PID + value: "0" + {{ end }} + - name: HOST_NET + value: "1" + - name: NODENAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: MDM + valueFrom: + secretKeyRef: + name: {{ .Release.Name }}-config + key: MDM + - name: MODE + value: "monitoring" + volumeMounts: + - name: dev + mountPath: /dev + - name: os-release + mountPath: /host-os-release + - name: sdc-storage + mountPath: /storage + - name: udev-d + mountPath: /rules.d + - name: host-opt-emc-path + mountPath: /host_opt_emc_path + {{- end }} + {{- if or (not (hasKey (.Values.node.sdc) "enabled")) (eq .Values.node.sdc.enabled true) }} + initContainers: + - name: sdc + securityContext: + privileged: true + image: {{ required "Must provide the PowerFlex SDC container image." .Values.images.powerflexSdc.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + env: + - name: NODENAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: MODE + value: "config" + - name: MDM + valueFrom: + secretKeyRef: + name: {{ .Release.Name }}-config + key: MDM + - name: HOST_DRV_CFG_PATH + value: /opt/emc/scaleio/sdc/bin + volumeMounts: + - name: dev + mountPath: /dev + - name: os-release + mountPath: /host-os-release + - name: host-opt-emc-path + mountPath: /host_opt_emc_path + - name: sdc-storage + mountPath: /storage + - name: udev-d + mountPath: /rules.d + - name: scaleio-path-opt + mountPath: /host_drv_cfg_path + {{- end }} + volumes: + - name: registration-dir + hostPath: + path: {{ .Values.kubeletConfigDir }}/plugins_registry/ + type: DirectoryOrCreate + - name: driver-path + hostPath: + path: {{ .Values.kubeletConfigDir }}/plugins/vxflexos.emc.dell.com + type: DirectoryOrCreate + - name: disks-path + hostPath: + path: {{ .Values.kubeletConfigDir }}/plugins/vxflexos.emc.dell.com/disks + type: DirectoryOrCreate + - name: volumedevices-path + hostPath: + path: {{ .Values.kubeletConfigDir }}/plugins/kubernetes.io/csi/volumeDevices + type: DirectoryOrCreate + - name: pods-path + hostPath: + path: {{ .Values.kubeletConfigDir }}/pods + type: Directory + - name: noderoot + hostPath: + path: / + type: Directory + - name: dev + hostPath: + path: /dev + type: Directory + - name: scaleio-path-opt + hostPath: + path: /opt/emc/scaleio/sdc/bin + type: DirectoryOrCreate + - name: sdc-storage + hostPath: + path: /var/emc-scaleio + type: DirectoryOrCreate + - name: udev-d + hostPath: + path: /etc/udev/rules.d + type: Directory + - name: os-release + hostPath: + path: /etc/os-release + type: File + - name: host-opt-emc-path + hostPath: + path: /opt/emc + type: Directory + - name: vxflexos-config + secret: + secretName: {{ .Release.Name }}-config + - name: vxflexos-config-params + configMap: + name: {{ .Release.Name }}-config-params +{{- if ge (int .Values.certSecretCount) 1 }} + - name: certs + projected: + sources: +{{- range $i, $e := until (int .Values.certSecretCount ) }} + - secret: + name: {{ print $.Release.Name "-certs-" $e }} + items: + - key: cert-{{ $e }} + path: cert-{{ $e }} +{{- end }} +{{- end }} +{{- if hasKey .Values "authorization" }} +{{- if eq .Values.authorization.enabled true }} + - name: karavi-authorization-config + secret: + secretName: karavi-authorization-config + - name: proxy-server-root-certificate + secret: + secretName: proxy-server-root-certificate +{{ end }} +{{ end }} +{{- if hasKey .Values "podmon" }} +{{- if eq .Values.podmon.enabled true }} + - name: usr-bin + hostPath: + path: /usr/bin + type: Directory + - name: kubelet-pods + hostPath: + path: /var/lib/kubelet/pods + type: Directory + - name: var-run + hostPath: + path: /var/run + type: Directory +{{- end }} +{{- end }} diff --git a/charts/dell/csi-vxflexos/2.13.0/values.yaml b/charts/dell/csi-vxflexos/2.13.0/values.yaml new file mode 100644 index 0000000000..d985dc6217 --- /dev/null +++ b/charts/dell/csi-vxflexos/2.13.0/values.yaml @@ -0,0 +1,378 @@ +--- +# k8s/driver attributes + +# "version" is used to verify the values file matches driver version +# Not recommend to change +version: v2.13.0 + +# "images" defines every container images used for the driver and its sidecars. +# To use your own images, or a private registry, change the values here. + +images: + # "driver" defines the container image, used for the driver container. + driver: + image: quay.io/dell/container-storage-modules/csi-vxflexos:v2.13.0 + # "powerflexSdc" defines the SDC image for init container. + powerflexSdc: + image: quay.io/dell/storage/powerflex/sdc:4.5.2.1 + # CSI sidecars + attacher: + image: registry.k8s.io/sig-storage/csi-attacher:v4.8.0 + provisioner: + image: registry.k8s.io/sig-storage/csi-provisioner:v5.1.0 + snapshotter: + image: registry.k8s.io/sig-storage/csi-snapshotter:v8.2.0 + resizer: + image: registry.k8s.io/sig-storage/csi-resizer:v1.13.1 + registrar: + image: registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.13.0 + healthmonitor: + image: registry.k8s.io/sig-storage/csi-external-health-monitor-controller:v0.14.0 + # CSM sidecars + replication: + image: quay.io/dell/container-storage-modules/dell-csi-replicator:v1.11.0 + vgsnapshotter: + image: quay.io/dell/container-storage-modules/csi-volumegroup-snapshotter:v1.8.1 + podmon: + image: quay.io/dell/container-storage-modules/podmon:v1.12.0 + authorization: + image: quay.io/dell/container-storage-modules/csm-authorization-sidecar:v2.1.0 + +# Represents number of certificate secrets, which user is going to create for ssl authentication. (vxflexos-cert-0..vxflexos-cert-n) +# If user does not use certificate, set to 0 +certSecretCount: 0 + +# CSI driver log level +# Allowed values: "error", "warn"/"warning", "info", "debug" +# Default value: "info" +logLevel: "info" + +# CSI driver log format +# Allowed values: "TEXT" or "JSON" +# Default value: "TEXT" +logFormat: "TEXT" + +# Specify kubelet config dir path. +# Ensure that the config.yaml file is present at this path. +# Default value: /var/lib/kubelet +kubeletConfigDir: /var/lib/kubelet + +# "defaultFsType" is used to set the default FS type which will be used +# for mount volumes if FsType is not specified in the storage class +# Allowed values: ext4, xfs +# Default value: none +defaultFsType: ext4 + +# imagePullPolicy: Policy to determine if the image should be pulled prior to starting the container. +# Allowed values: +# Always: Always pull the image. +# IfNotPresent: Only pull the image if it does not already exist on the node. +# Never: Never pull the image. +# Default value: None +imagePullPolicy: IfNotPresent + +# externalAccess: allows to specify additional entries for hostAccess of NFS volumes. Both single IP address and subnet are valid entries. +# Allowed Values: x.x.x.x/xx or x.x.x.x +# Default Value: None +externalAccess: + +# enableQuota: a boolean that, when enabled, will set quota limit for a newly provisioned NFS volume. +# Allowed values: +# true: set quota for volume +# false: do not set quota for volume +# Optional: true +# Default value: none +enableQuota: false + +# "enablesnapshotcgdelete"- a boolean that, when enabled, will delete all snapshots in a consistency group +# everytime a snap in the group is deleted +# Allowed values: true, false +# Default value: none +enablesnapshotcgdelete: "false" + +# "enablelistvolumesnapshot" - a boolean that, when enabled, will allow list volume operation to include snapshots (since creating a volume +# from a snap actually results in a new snap) +# It is recommended this be false unless instructed otherwise. +# Allowed values: true, false +# Default value: none +enablelistvolumesnapshot: "false" + +# Setting allowRWOMultiPodAccess to "true" will allow multiple pods on the same node +# to access the same RWO volume. This behavior conflicts with the CSI specification version 1.3 +# NodePublishVolume descrition that requires an error to be returned in this case. +# However, some other CSI drivers support this behavior and some customers desire this behavior. +# Kubernetes could make a change at their discretion that would preclude our ability to support this option. +# Customers use this option at their own risk. +# You should leave this set as "false" unless instructed to change it by Dell support. +# Allowed values: true, false +# Default value: "false" +allowRWOMultiPodAccess: "false" + +# fsGroupPolicy: Defines if the underlying volume supports changing ownership and permission of the volume before being mounted. +# Allowed values: +# ReadWriteOnceWithFSType: supports volume ownership and permissions change only if the fsType is defined +# and the volume's accessModes contains ReadWriteOnce. +# File: kubernetes may use fsGroup to change permissions and ownership of the volume +# to match user requested fsGroup in the pod's security policy regardless of fstype or access mode. +# None: volumes will be mounted with no modifications. +fsGroupPolicy: File + +# maxVxflexosVolumesPerNode: Specify default value for maximum number of volumes that controller can publish to the node. +# If value is zero CO SHALL decide how many volumes of this type can be published by the controller to the node. +# This limit is applicable to all the nodes in the cluster for which node label 'maxVxflexosVolumesPerNode' is not set. +# Allowed values: n, where n >= 0 +# Default value: 0 +maxVxflexosVolumesPerNode: 0 + +# "controller" allows to configure controller specific parameters +controller: + # replication: allows to configure replication + # Replication CRDs must be installed before installing driver + replication: + # enabled: Enable/Disable replication feature + # Allowed values: + # true: enable replication feature(install dell-csi-replicator sidecar) + # false: disable replication feature(do not install dell-csi-replicator sidecar) + # Default value: false + enabled: false + + # replicationContextPrefix: prefix to use for naming of resources created by replication feature + # Allowed values: string + # Default value: powerflex + replicationContextPrefix: "powerflex" + + # replicationPrefix: prefix to prepend to storage classes parameters + # Allowed values: string + # Default value: replication.storage.dell.com + replicationPrefix: "replication.storage.dell.com" + + healthMonitor: + # enabled: Enable/Disable health monitor of CSI volumes + # Allowed values: + # true: enable checking of health condition of CSI volumes + # false: disable checking of health condition of CSI volumes + # Default value: None + enabled: false + + # interval: Interval of monitoring volume health condition + # Allowed values: Number followed by unit (s,m,h) + # Examples: 60s, 5m, 1h + # Default value: 60s + interval: 60s + + # volumeNamePrefix- defines a string prepended to each volume created by the CSI driver. + # Default value: csivol + # Examples: "k8s", "app1" + volumeNamePrefix: csivol + + # "controllerCount" defines the number of VxFlex controller pods to deploy + # Allowed values: n, where n > 0 + # Default value: none + controllerCount: 2 + + snapshot: + # enabled: Enable/Disable volume snapshot feature + # Allowed values: + # true: enable volume snapshot feature(install snapshotter sidecar) + # false: disable volume snapshot feature(do not install snapshotter sidecar) + # Default value: None + enabled: true + + resizer: + # enabled: Enable/Disable volume expansion feature + # Allowed values: + # true: enable volume expansion feature(install resizer sidecar) + # false: disable volume snapshot feature(do not install resizer sidecar) + # Default value: None + enabled: true + + # "controller.nodeSelector" defines what nodes would be selected for pods of controller deployment + # Leave as blank to use all nodes + # Allowed values: map of key-value pairs + # Default value: None + # Examples: + # node-role.kubernetes.io/control-plane: "" + nodeSelector: + # node-role.kubernetes.io/control-plane: "" + + # "controller.tolerations" defines tolerations that would be applied to controller deployment + # Leave as blank to install controller on worker nodes + # Default value: None + tolerations: + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/master taint + # - key: "node-role.kubernetes.io/master" + # operator: "Exists" + # effect: "NoSchedule" + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/control-plane taint + # - key: "node-role.kubernetes.io/control-plane" + # operator: "Exists" + # effect: "NoSchedule" + +# "node" allows to configure node specific parameters +node: + healthMonitor: + # enabled: Enable/Disable health monitor of CSI volumes- volume usage, volume condition + # Allowed values: + # true: enable checking of health condition of CSI volumes + # false: disable checking of health condition of CSI volumes + # Default value: None + enabled: false + + # "node.nodeSelector" defines what nodes would be selected for pods of node daemonset + # Leave as blank to use all nodes + # Allowed values: map of key-value pairs + # Default value: None + # Examples: + # node-role.kubernetes.io/control-plane: "" + nodeSelector: + # node-role.kubernetes.io/control-plane: "" + + # "node.tolerations" defines tolerations that would be applied to node daemonset + # Leave as blank to install node driver only on worker nodes + # Default value: None + tolerations: + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/master taint + # - key: "node-role.kubernetes.io/master" + # operator: "Exists" + # effect: "NoSchedule" + # Uncomment if nodes you wish to use have the node-role.kubernetes.io/control-plane taint + # - key: "node-role.kubernetes.io/control-plane" + # operator: "Exists" + # effect: "NoSchedule" + # Uncomment if CSM for Resiliency and CSI Driver pods monitor is enabled + # - key: "offline.vxflexos.storage.dell.com" + # operator: "Exists" + # effect: "NoSchedule" + # - key: "vxflexos.podmon.storage.dell.com" + # operator: "Exists" + # effect: "NoSchedule" + # - key: "offline.unity.storage.dell.com" + # operator: "Exists" + # effect: "NoSchedule" + # - key: "unity.podmon.storage.dell.com" + # operator: "Exists" + # effect: "NoSchedule" + # - key: "offline.isilon.storage.dell.com" + # operator: "Exists" + # effect: "NoSchedule" + # - key: "isilon.podmon.storage.dell.com" + # operator: "Exists" + # effect: "NoSchedule" + + sdc: + # enabled: Enable/Disable SDC + enabled: true + + # "renameSDC" defines the rename operation for SDC + # Default value: None + renameSDC: + # enabled: Enable/Disable rename of SDC + # Allowed values: + # true: enable renaming + # false: disable renaming + # Default value: "false" + enabled: false + # "prefix" defines a string for prefix of the SDC name. + # "prefix" + "worker_node_hostname" should not exceed 31 chars. + # Default value: none + # Examples: "rhel-sdc", "sdc-test" + prefix: "sdc-test" + + # "approveSDC" defines the approve operation for SDC + # Default value: None + approveSDC: + # enabled: Enable/Disable SDC approval + # Allowed values: + # true: Driver will attempt to approve restricted SDC by GUID during setup + # false: Driver will not attempt to approve restricted SDC by GUID during setup + # Default value: false + enabled: false + +# Storage Capacity Tracking +# Note: Capacity tracking is supported in kubernetes v1.24 and above, this feature will be automatically disabled in older versions. +storageCapacity: + # enabled : Enable/Disable storage capacity tracking + # Allowed values: + # true: enable storage capacity tracking + # false: disable storage capacity tracking + # Default value: true + enabled: true + # pollInterval : Configure how often external-provisioner polls the driver to detect changed capacity + # Allowed values: 1m,2m,3m,...,10m,...,60m etc + # Default value: 5m + pollInterval: 5m + +# monitoring pod details +# These options control the running of the monitoring container +# This container gather diagnostic information in case of failure +monitor: + # enabled allows the usage of the monitoring pod to be disabled + # Allowed values: true, false + # Default value: "false" + enabled: false + + # hostNetwork determines if the monitor pod should run on the host network or not + # Allowed values: true, false + # Default value: "false" + hostNetwork: true + + # hostPID determines if the monitor pod should run in the host namespace + # Allowed values: true, false + # Default value: "false" + hostPID: true + +# CSM module attributes + +# volume group snapshotter(vgsnapshotter) details +# These options control the running of the vgsnapshotter container +vgsnapshotter: + enabled: false + +# Podmon is an optional feature under development and tech preview. +# Enable this feature only after contact support for additional information +podmon: + enabled: false + + controller: + args: + - "--csisock=unix:/var/run/csi/csi.sock" + - "--labelvalue=csi-vxflexos" + - "--mode=controller" + - "--skipArrayConnectionValidation=false" + - "--driver-config-params=/vxflexos-config-params/driver-config-params.yaml" + - "--driverPodLabelValue=dell-storage" + - "--ignoreVolumelessPods=false" + node: + args: + - "--csisock=unix:/var/lib/kubelet/plugins/vxflexos.emc.dell.com/csi_sock" + - "--labelvalue=csi-vxflexos" + - "--mode=node" + - "--leaderelection=false" + - "--driver-config-params=/vxflexos-config-params/driver-config-params.yaml" + - "--driverPodLabelValue=dell-storage" + - "--ignoreVolumelessPods=false" + +# CSM module attributes +# authorization: enable csm-authorization for RBAC +# Deploy and configure authorization before installing driver +# Allowed values: +# "true" - authorization is enabled +# "false" - authorization is disabled +# Default value: "false" +authorization: + enabled: false + + # proxyHost: hostname of the csm-authorization server + # Default value: None + proxyHost: + + # skipCertificateValidation: certificate validation of the csm-authorization server + # Allowed Values: + # "true" - TLS certificate verification will be skipped + # "false" - TLS certificate will be verified + # Default value: "true" + skipCertificateValidation: true + +interfaceNames: + # worker1: interface1 + # worker2: interface2 diff --git a/index.yaml b/index.yaml index c28809cbaa..85c4e3fe35 100644 --- a/index.yaml +++ b/index.yaml @@ -9974,6 +9974,32 @@ entries: - assets/crate/crate-operator-2.28.0.tgz version: 2.28.0 csi-isilon: + - annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Dell CSI PowerScale + catalog.cattle.io/kube-version: '>= 1.21.0' + catalog.cattle.io/release-name: isilon + apiVersion: v2 + appVersion: 2.13.0 + created: "2025-01-17T00:01:59.316084565Z" + description: 'PowerScale CSI (Container Storage Interface) driver Kubernetes integration. + This chart includes everything required to provision via CSI as well as an Isilon + StorageClass. ' + digest: 2303a36d7997d3c92d8794ec0e968c7d157328a68ab4896d054acc3d4d439f1f + icon: file://assets/icons/csi-isilon.png + keywords: + - csi + - storage + kubeVersion: '>= 1.21.0' + maintainers: + - name: DellEMC + name: csi-isilon + sources: + - https://github.com/dell/csi-isilon + type: application + urls: + - assets/dell/csi-isilon-2.13.0.tgz + version: 2.13.0 - annotations: catalog.cattle.io/certified: partner catalog.cattle.io/display-name: Dell CSI PowerScale @@ -10156,6 +10182,38 @@ entries: - assets/dell/csi-isilon-2.7.0.tgz version: 2.7.0 csi-powermax: + - annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Dell CSI PowerMax + catalog.cattle.io/kube-version: '>= 1.23.0' + catalog.cattle.io/release-name: "" + apiVersion: v2 + appVersion: 2.13.0 + created: "2025-01-17T00:01:59.32350698Z" + dependencies: + - condition: required + name: csireverseproxy + repository: "" + version: 2.12.0 + description: 'PowerMax CSI (Container Storage Interface) driver Kubernetes integration. + This chart includes everything required to provision via CSI as well as a PowerMax + StorageClass. ' + digest: 5cc2a6ede33a9eed1d5512804553b447812d86df6bd09445cd1da1dffab99d22 + home: https://github.com/dell/csi-powermax + icon: file://assets/icons/csi-powermax.png + keywords: + - csi + - storage + kubeVersion: '>= 1.23.0' + maintainers: + - name: DellEMC + name: csi-powermax + sources: + - https://github.com/dell/csi-powermax + type: application + urls: + - assets/dell/csi-powermax-2.13.0.tgz + version: 2.13.0 - annotations: catalog.cattle.io/certified: partner catalog.cattle.io/display-name: Dell CSI PowerMax @@ -10413,6 +10471,33 @@ entries: - assets/dell/csi-powermax-2.7.0.tgz version: 2.7.0 csi-powerstore: + - annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Dell CSI PowerStore + catalog.cattle.io/kube-version: '>= 1.24.0' + catalog.cattle.io/release-name: powerstore + apiVersion: v2 + appVersion: 2.13.0 + created: "2025-01-17T00:01:59.332836908Z" + description: 'PowerStore CSI (Container Storage Interface) driver Kubernetes integration. + This chart includes everything required to provision via CSI as well as a PowerStore + StorageClass. ' + digest: 1f791dbecb796e81bb76ee2910222e80e0216df4b9ae32831af91ca0e06b93a5 + home: https://github.com/dell/csi-powerstore + icon: file://assets/icons/csi-powerstore.png + keywords: + - csi + - storage + kubeVersion: '>= 1.24.0' + maintainers: + - name: DellEMC + name: csi-powerstore + sources: + - https://github.com/dell/csi-powerstore + type: application + urls: + - assets/dell/csi-powerstore-2.13.0.tgz + version: 2.13.0 - annotations: catalog.cattle.io/certified: partner catalog.cattle.io/display-name: Dell CSI PowerStore @@ -10629,6 +10714,32 @@ entries: - assets/dell/csi-powerstore-2.7.0.tgz version: 2.7.0 csi-unity: + - annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Dell CSI Unity + catalog.cattle.io/kube-version: '>= 1.24.0' + catalog.cattle.io/release-name: unity + apiVersion: v2 + appVersion: 2.13.0 + created: "2025-01-17T00:01:59.339463541Z" + description: 'Unity XT CSI (Container Storage Interface) driver Kubernetes integration. + This chart includes everything required to provision via CSI as well as a Unity + XT StorageClass. ' + digest: 6942515d5e297fb63c80ed671bb1ec8e60677c0e8b4a7fbb514e3f7284ddac75 + icon: file://assets/icons/csi-unity.png + keywords: + - csi + - storage + kubeVersion: '>= 1.24.0' + maintainers: + - name: DellEMC + name: csi-unity + sources: + - https://github.com/dell/csi-unity + type: application + urls: + - assets/dell/csi-unity-2.13.0.tgz + version: 2.13.0 - annotations: catalog.cattle.io/certified: partner catalog.cattle.io/display-name: Dell CSI Unity @@ -10837,6 +10948,32 @@ entries: - assets/dell/csi-unity-2.7.0.tgz version: 2.7.0 csi-vxflexos: + - annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Dell CSI PowerFlex + catalog.cattle.io/kube-version: '>= 1.21.0' + catalog.cattle.io/namespace: vxflexos + catalog.cattle.io/release-name: vxflexos + apiVersion: v2 + appVersion: 2.13.0 + created: "2025-01-17T00:01:59.345125143Z" + description: 'VxFlex OS CSI (Container Storage Interface) driver Kubernetes integration. + This chart includes everything required to provision via CSI as well as a VxFlex + OS StorageClass. ' + digest: 7f09b3e4bc2356731128cdc0253723d7efeccf7916e0d3cb8e0defc249184a65 + icon: file://assets/icons/csi-vxflexos.png + keywords: + - csi + - storage + kubeVersion: '>= 1.21.0' + maintainers: + - name: DellEMC + name: csi-vxflexos + sources: + - https://github.com/dell/csi-vxflexos + urls: + - assets/dell/csi-vxflexos-2.13.0.tgz + version: 2.13.0 - annotations: catalog.cattle.io/certified: partner catalog.cattle.io/display-name: Dell CSI PowerFlex @@ -22797,6 +22934,38 @@ entries: - assets/kuma/kuma-2.2.2.tgz version: 2.2.2 linkerd-control-plane: + - 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-25.1.1 + created: "2025-01-17T00:01:58.766685559Z" + 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: 907dc56083e93faf9eb4abc94d548e0d78c64cf3070e323779cb48393fc7a8e0 + 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-2025.1.1.tgz + version: 2025.1.1 - annotations: catalog.cattle.io/auto-install: linkerd-crds catalog.cattle.io/certified: partner @@ -24387,6 +24556,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: "2025-01-17T00:01:58.898546379Z" + 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: 7d3bc287c8fd9b24bab0170b5e1334efbe2a32dad3c81e2fa1c06c85cde5c4f7 + 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-2025.1.1.tgz + version: 2025.1.1 - annotations: catalog.cattle.io/certified: partner catalog.cattle.io/display-name: Linkerd CRDs @@ -47438,4 +47637,4 @@ entries: urls: - assets/netfoundry/ziti-host-1.5.1.tgz version: 1.5.1 -generated: "2025-01-16T00:02:00.200615064Z" +generated: "2025-01-17T00:01:58.398652304Z"